Rob Crittenden wrote:
Martin Kosek wrote:
On Fri, 2011-06-17 at 17:06 -0400, Rob Crittenden wrote:
A dogtag replica file is created as usual. When the replica is installed
dogtag is optional and not installed by default. Adding the --setup-ca
option will configure it when the replica is installed.

A new tool ipa-ca-install will configure dogtag if it wasn't configured
when the replica was initially installed.

https://fedorahosted.org/freeipa/ticket/1251

See the ticket for testing suggestions.

rob

I have found some issues with the patch:

1) Man page:
- missing man file in man folder's Makefile.am
- missing man file in the spec -> man is not installed

Yeah, I realized that after I submitted it.


2) Missing ipa-ca-install in install/po/Makefile.in

Oh, ipa-dns-install is missing too, I'll fix it.


3) ipa-ca-install:
- expand_info, read_info, get_host_name or install_ca: functions are
copied from ipa-replica-install tool. Having a lot of redundant code
leads to the dark side. Calling these functions from a common library
seems more convenient to me.

Yeah, I'll see about pulling some of that into installutils.py.
install_ca is different depending on context though, I'll have to see
how complex the conditionals become if I combine them.


4) man ipa-ca-install:

+\fB\-p\fR, \fB\-\-password\fR=\fIDM_PASSWORD\fR

is not consistent with

+\fB\-w\fR \fIADMIN_PASSWORD\fR, \fB\-\-admin\-password\fR=
\fIADMIN_PASSWORD\fR

(missing DM_PASSWORD placeholder after "-p")

Ok, we'll need to check the ipa-replica-install man page too, I based
this on that.



5) Now the real problem - when I am installing a replica I got a strange
error:

#
ipa-replica-install
/home/mkosek/replica-info-vm-060.idm.lab.bos.redhat.com.gpg --setup-ca
-w secret123
Directory Manager (existing master) password:

Run connection check to master
Check connection from replica to remote master
'vm-099.idm.lab.bos.redhat.com':
Directory Service: Unsecure port (389): OK
Directory Service: Secure port (636): OK
Kerberos (88): OK
PKI-CA: Directory Service port (7389): OK
PKI-CA: Agent secure port (9443): OK
PKI-CA: EE secure port (9444): OK
PKI-CA: Admin secure port (9445): OK
PKI-CA: EE secure client auth port (9446): OK
PKI-CA: Unsecure port (9180): OK

Connection from replica to master is OK.
Start listening on required ports for remote master check
Get credentials to log in to remote master
Execute check on remote master
Check connection from master to remote replica
'vm-060.idm.lab.bos.redhat.com':
Directory Service: Unsecure port (389): OK
Directory Service: Secure port (636): OK
Kerberos (88): OK
PKI-CA: Directory Service port (7389): OK
PKI-CA: Agent secure port (9443): OK
PKI-CA: EE secure port (9444): OK
PKI-CA: Admin secure port (9445): OK
PKI-CA: EE secure client auth port (9446): OK
PKI-CA: Unsecure port (9180): OK

Connection from master to replica is OK.

Connection check OK
Configuring ntpd
[1/4]: stopping ntpd
[2/4]: writing configuration
[3/4]: configuring ntpd to start on boot
[4/4]: starting ntpd
done configuring ntpd.
Configuring directory server for the CA: Estimated time 30 seconds
[1/3]: creating directory server user
[2/3]: creating directory server instance
[3/3]: restarting directory server
done configuring pkids.
creation of replica failed: Incorrect padding

Your system may be partly configured.
Run /usr/sbin/ipa-server-install --uninstall to clean up.


/var/log/ipareplica-install.log:
...
2011-06-23 08:37:35,907 DEBUG args=/usr/bin/certutil
-d /etc/dirsrv/slapd-PKI-IPA/ -L -n Server-Cert -a
2011-06-23 08:37:35,908 DEBUG stdout=-----BEGIN CERTIFICATE-----
MIIDnjCCAoagAwIBAgIBEDANBgkqhkiG9w0BAQsFADBBMR8wHQYDVQQKExZJRE0u^M
TEFCLkJPUy5SRURIQVQuQ09NMR4wHAYDVQQDExVDZXJ0aWZpY2F0ZSBBdXRob3Jp^M
dHkwHhcNMTEwNjIzMTIzNjM0WhcNMTExMjIwMTIzNjM0WjBJMR8wHQYDVQQKExZJ^M
RE0uTEFCLkJPUy5SRURIQVQuQ09NMSYwJAYDVQQDEx12bS0wNjAuaWRtLmxhYi5i^M
b3MucmVkaGF0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMM^M
8FypUbIwR0NRcIEJ5GHbL54D5gh0ao5PoA8LRmcz6QdMjDtA/1aeg9fskdkQ6Peh^M
TTjlvL5Y9b/TVDxx4KrzbMiBCDdMecsbUSK32pJjw6DJCFhcBTwuAj/zZIrvsicT^M
jtnTmeRQCEqGjRmizQHCDDdh+zx0Rh3mbzmxsZ4XaSafksm/y3tMBbw2S0Q7agNF^M
3Z95qQH9CZ1ManH90zMjOwJxknpxGrwaou9OsPJ1b7M6cvBVLW9kuEDO4c7qTcqa^M
h7BRDQD/XVQn31/UFyLRxl+F4cTp6eBhb9B1+Mv18ZAw9xNhpb1xsWsNDqLh0zY4^M
5ZeUKTkZS4+WuJOYHFUCAwEAAaOBmDCBlTAfBgNVHSMEGDAWgBQZX7pLjCg+Fol2^M
vkqZQBQRB7w67jBNBggrBgEFBQcBAQRBMD8wPQYIKwYBBQUHMAGGMWh0dHA6Ly92^M
bS0wOTkuaWRtLmxhYi5ib3MucmVkaGF0LmNvbTo5MTgwL2NhL29jc3AwDgYDVR0P^M
AQH/BAQDAgTwMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBCwUAA4IB^M
AQBzy0uiVeNGZpUHolgOsyKRl4Q3gpZg/25ai8HHylLSSjYXqy5WmNBy4NPIbVe8^M
p6ZAjW7Lc5BwNTWwkbJoB9JTmhyIRRCWO1hf3qZC1eO9/Ax7XN2nCXka6NRoSxz7^M
Ci7G6RsqM/egbBCUqgbRNz4DJntcrOdFYaOK03Jpfl0lsW0B6l2d+rIuZI5uVK/0^M
uPsKdjCemzVsMOySBchnd/Cy8mXiP6ah7FZIpi9rZScA+UjTUou6PDGcft6jyAj9^M
oeqol6t/6Otd+OFbAYwlccG73rq49sOB9GTjSQelMrHK/hunxIczwYrK2ZHvw2Hy^M
HMOJrmcjFGoa/eL65JwmiFVl
-----END CERTIFICATE-----

2011-06-23 08:37:35,908 DEBUG stderr=
2011-06-23 08:37:35,914 DEBUG Incorrect padding
File "/usr/sbin/ipa-replica-install", line 560, in<module>
main()

File "/usr/sbin/ipa-replica-install", line 502, in main
(CA, cs) = install_ca(config)

File "/usr/sbin/ipa-replica-install", line 173, in install_ca
cs.load_pkcs12()

File
"/usr/lib/python2.7/site-packages/ipaserver/install/cainstance.py", line
325, in load_pkcs12
self.dercert = dsdb.get_cert_from_db(self.nickname, pem=False)

File "/usr/lib/python2.7/site-packages/ipaserver/install/certs.py",
line 449, in get_cert_from_db
dercert = base64.b64decode(cert)

File "/usr/lib64/python2.7/base64.py", line 76, in b64decode
raise TypeError(msg)


Any idea what could cause this? This was run on clean VMs with your
patch on top of master branch.

It means that the blob I ended up with wasn't properly base64-encoded.
It could mean I missed a header/footer or something else. I'll see if I
can reproduce.

I think I've addressed all your concerns. I wasn't able to reproduce the crash but I can see what caused it: we passed in a cert with a header/footer to base64.b64decode(). I added a call to x509.strip_header() which should fix it up.

rob
>From 13d0eefb6ad903ea74ce802b20f62d8bbf5c1a52 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcrit...@redhat.com>
Date: Fri, 17 Jun 2011 16:47:39 -0400
Subject: [PATCH] Make dogtag an optional (and default un-) installed component in a replica.

A dogtag replica file is created as usual. When the replica is installed
dogtag is optional and not installed by default. Adding the --setup-ca
option will configure it when the replica is installed.

A new tool ipa-ca-install will configure dogtag if it wasn't configured
when the replica was initially installed.

This moves a fair bit of code out of ipa-replica-install into
installutils and cainstance to avoid duplication.

https://fedorahosted.org/freeipa/ticket/1251
---
 freeipa.spec.in                         |    5 +
 install/po/Makefile.in                  |    2 +
 install/tools/Makefile.am               |    1 +
 install/tools/ipa-ca-install            |  183 +++++++++++++++++++++++++++++++
 install/tools/ipa-replica-install       |  151 ++++----------------------
 install/tools/man/Makefile.am           |    1 +
 install/tools/man/ipa-ca-install.1      |   49 ++++++++
 install/tools/man/ipa-replica-install.1 |    2 +-
 ipaserver/install/cainstance.py         |   78 +++++++++++++-
 ipaserver/install/certs.py              |    4 +-
 ipaserver/install/installutils.py       |   57 ++++++++++
 ipaserver/install/replication.py        |   33 ++++++
 12 files changed, 433 insertions(+), 133 deletions(-)
 create mode 100755 install/tools/ipa-ca-install
 create mode 100644 install/tools/man/ipa-ca-install.1

diff --git a/freeipa.spec.in b/freeipa.spec.in
index 36281f8d39b59e627e01fed971917325759cd7b4..400084d4a66c46ef95fa0f44d8ec4384bf994694 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -356,6 +356,7 @@ fi
 %files server
 %defattr(-,root,root,-)
 %doc COPYING README Contributors.txt
+%{_sbindir}/ipa-ca-install
 %{_sbindir}/ipa-dns-install
 %{_sbindir}/ipa-server-install
 %{_sbindir}/ipa-replica-conncheck
@@ -436,6 +437,7 @@ fi
 %{_mandir}/man1/ipa-server-certinstall.1.gz
 %{_mandir}/man1/ipa-server-install.1.gz
 %{_mandir}/man1/ipa-dns-install.1.gz
+%{_mandir}/man1/ipa-ca-install.1.gz
 %{_mandir}/man1/ipa-compat-manage.1.gz
 %{_mandir}/man1/ipa-nis-manage.1.gz
 %{_mandir}/man1/ipa-host-net-manage.1.gz
@@ -498,6 +500,9 @@ fi
 %ghost %attr(0644,root,apache) %config(noreplace) %{_sysconfdir}/ipa/default.conf
 
 %changelog
+* Fri Jun 17 2011 Rob Crittenden <rcrit...@redhat.com> - 2.0.90-4
+- Ship ipa-ca-install utility
+
 * Thu May 12 2011 Rob Crittenden <rcrit...@redhat.com> - 2.0.90-3
 - Set min nvr of selinux-policy to 3.9.16-18 on F-15+
 - Set min nvr of pki-ca to 9.0.7 on F-15+
diff --git a/install/po/Makefile.in b/install/po/Makefile.in
index a1e804f9d89aad61537d761f85b7d82bddb93357..a5468752723636b005c1d0876f10326e5c970814 100644
--- a/install/po/Makefile.in
+++ b/install/po/Makefile.in
@@ -53,6 +53,8 @@ PY_EXPLICIT_FILES = \
      install/tools/ipa-host-net-manage \
      install/tools/ipa-server-install \
      install/tools/ipa-ldap-updater \
+     install/tools/ipa-dns-install \
+     install/tools/ipa-ca-install \
      ipa-client/ipa-install/ipa-client-install
 
 PYTHON_POTFILES = $(PY_FILES) $(PY_EXPLICIT_FILES)
diff --git a/install/tools/Makefile.am b/install/tools/Makefile.am
index 9004bb248d9ace2c683f04d15da85b401411f93d..c6ecd92876adb5ba5dd5eef041502c27e56bb811 100644
--- a/install/tools/Makefile.am
+++ b/install/tools/Makefile.am
@@ -5,6 +5,7 @@ SUBDIRS = 			\
         $(NULL)
 
 sbin_SCRIPTS =			\
+	ipa-ca-install		\
 	ipa-dns-install		\
 	ipa-server-install	\
 	ipa-replica-conncheck	\
diff --git a/install/tools/ipa-ca-install b/install/tools/ipa-ca-install
new file mode 100755
index 0000000000000000000000000000000000000000..83cb6e00087eb5c6b3798cfe24d7925c1c88af18
--- /dev/null
+++ b/install/tools/ipa-ca-install
@@ -0,0 +1,183 @@
+#! /usr/bin/python -E
+# Authors: Rob Crittenden <rcrit...@redhat.com>
+#
+# Copyright (C) 2011  Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+import sys
+import socket
+
+import os, traceback, logging, shutil
+
+from ipapython import ipautil
+
+from ipaserver.install import installutils, service
+from ipaserver.install import certs
+from ipaserver.install.installutils import HostnameLocalhost
+from ipaserver.install.installutils import ReplicaConfig, expand_replica_info, read_replica_info
+from ipaserver.install.installutils import get_host_name
+from ipaserver.install import dsinstance, cainstance
+from ipaserver.install.replication import replica_conn_check
+from ipapython import version
+from ipalib import api, util
+from ipapython.config import IPAOptionParser
+from ipapython import sysrestore
+
+CACERT="/etc/ipa/ca.crt"
+REPLICA_INFO_TOP_DIR=None
+
+def parse_options():
+    usage = "%prog [options] REPLICA_FILE"
+    parser = IPAOptionParser(usage=usage, version=version.VERSION)
+    parser.add_option("-d", "--debug", dest="debug", action="store_true",
+                      default=False, help="gather extra debugging information")
+    parser.add_option("-p", "--password", dest="password", sensitive=True,
+                      help="Directory Manager (existing master) password")
+    parser.add_option("-w", "--admin-password", dest="admin_password", sensitive=True,
+                      help="Admin user Kerberos password used for connection check")
+    parser.add_option("--no-host-dns", dest="no_host_dns", action="store_true",
+                      default=False,
+                      help="Do not use DNS for hostname lookup during installation")
+    parser.add_option("--skip-conncheck", dest="skip_conncheck", action="store_true",
+                      default=False, help="skip connection check to remote master")
+    parser.add_option("-U", "--unattended", dest="unattended", action="store_true",
+                      default=False, help="unattended installation never prompts the user")
+
+    options, args = parser.parse_args()
+    safe_options = parser.get_safe_opts(options)
+
+    if len(args) != 1:
+        parser.error("you must provide a file generated by ipa-replica-prepare")
+
+    return safe_options, options, args[0]
+
+def get_dirman_password():
+    return installutils.read_password("Directory Manager (existing master)", confirm=False, validate=False)
+
+def main():
+    safe_options, options, filename = parse_options()
+    installutils.standard_logging_setup("/var/log/ipareplica-ca-install.log", options.debug)
+    logging.debug('%s was invoked with argument "%s" and options: %s' % (sys.argv[0], filename, safe_options))
+
+    if not ipautil.file_exists(filename):
+        sys.exit("Replica file %s does not exist" % filename)
+
+    global sstore
+    sstore = sysrestore.StateFile('/var/lib/ipa/sysrestore')
+
+    if not dsinstance.DsInstance().is_configured():
+        sys.exit("IPA server is not configured on this system.\n")
+
+    # get the directory manager password
+    dirman_password = options.password
+    if not dirman_password:
+        if options.unattended:
+            sys.exit('Directory Manager password required')
+        try:
+            dirman_password = get_dirman_password()
+        except KeyboardInterrupt:
+            sys.exit(0)
+
+    if not options.admin_password and not options.skip_conncheck and \
+        options.unattended:
+            sys.exit('admin password required')
+
+    try:
+        top_dir, dir = expand_replica_info(filename, dirman_password)
+        global REPLICA_INFO_TOP_DIR
+        REPLICA_INFO_TOP_DIR = top_dir
+    except Exception, e:
+        print "ERROR: Failed to decrypt or open the replica file."
+        print "Verify you entered the correct Directory Manager password."
+        sys.exit(1)
+
+    config = ReplicaConfig()
+    read_replica_info(dir, config)
+    config.dirman_password = dirman_password
+    try:
+        host = get_host_name(options.no_host_dns)
+    except RuntimeError, e:
+        logging.error(str(e))
+        sys.exit(1)
+    if config.host_name != host:
+        try:
+            print "This replica was created for '%s' but this machine is named '%s'" % (config.host_name, host)
+            if not ipautil.user_input("This may cause problems. Continue?", True):
+                sys.exit(0)
+            config.host_name = host
+            print ""
+        except KeyboardInterrupt:
+            sys.exit(0)
+    config.dir = dir
+    config.setup_ca = True
+
+    if not options.skip_conncheck:
+        replica_conn_check(config.master_host_name, config.host_name, config.realm_name, True, options.admin_password)
+
+    api.bootstrap(in_server=True)
+    api.finalize()
+
+    # Configure the CA if necessary
+    (CA, cs) = cainstance.install_replica_ca(config, postinstall=True)
+
+    # We need to ldap_enable the CA now that DS is up and running
+    CA.ldap_enable('CA', config.host_name, config.dirman_password,
+                   util.realm_to_suffix(config.realm_name))
+    cs.add_simple_service('dogtagldap/%s@%s' % (config.host_name, config.realm_name))
+    cs.add_cert_to_service()
+
+    service.print_msg("Setting the certificate subject base")
+    CA.set_subject_in_config(util.realm_to_suffix(config.realm_name))
+
+try:
+    if not os.geteuid()==0:
+        sys.exit("\nYou must be root to run this script.\n")
+
+    main()
+    sys.exit(0)
+except SystemExit, e:
+    sys.exit(e)
+except socket.error, (errno, errstr):
+    print errstr
+except HostnameLocalhost:
+    print "The hostname resolves to the localhost address (127.0.0.1/::1)"
+    print "Please change your /etc/hosts file so that the hostname"
+    print "resolves to the ip address of your network interface."
+    print ""
+    print "Please fix your /etc/hosts file and restart the setup program"
+except Exception, e:
+    print "creation of replica failed: %s" % str(e)
+    message = str(e)
+    for str in traceback.format_tb(sys.exc_info()[2]):
+        message = message + "\n" + str
+    logging.debug(message)
+except KeyboardInterrupt:
+    print "Installation cancelled." 
+finally:
+    # always try to remove decrypted replica file
+    try:
+        if REPLICA_INFO_TOP_DIR:
+            shutil.rmtree(REPLICA_INFO_TOP_DIR)
+    except OSError:
+        pass
+
+print ""
+print "Your system may be partly configured." 
+print "Run /usr/sbin/ipa-server-install --uninstall to clean up."
+
+# the only way to get here is on error or ^C
+sys.exit(1)
diff --git a/install/tools/ipa-replica-install b/install/tools/ipa-replica-install
index c39d992de8c42a1d1e1e641e541aacb705946d40..219e54bee530c5a95c9751c2ee5cfb2acece3d3b 100755
--- a/install/tools/ipa-replica-install
+++ b/install/tools/ipa-replica-install
@@ -21,17 +21,19 @@
 import sys
 import socket
 
-import tempfile, os, pwd, traceback, logging, shutil
+import os, pwd, traceback, logging, shutil
 import grp
-from ConfigParser import SafeConfigParser
 
 from ipapython import ipautil
 
 from ipaserver.install import dsinstance, installutils, krbinstance, service
 from ipaserver.install import bindinstance, httpinstance, ntpinstance, certs
-from ipaserver.install.replication import check_replication_plugin
+from ipaserver.install.replication import check_replication_plugin, replica_conn_check
 from ipaserver.install.installutils import HostnameLocalhost, resolve_host
+from ipaserver.install.installutils import ReplicaConfig, expand_replica_info, read_replica_info
+from ipaserver.install.installutils import get_host_name
 from ipaserver.plugins.ldap2 import ldap2
+from ipaserver.install import cainstance
 from ipapython import version
 from ipalib import api, errors, util
 from ipapython.config import IPAOptionParser
@@ -40,16 +42,6 @@ from ipapython import sysrestore
 CACERT="/etc/ipa/ca.crt"
 REPLICA_INFO_TOP_DIR=None
 
-class ReplicaConfig:
-    def __init__(self):
-        self.realm_name = ""
-        self.domain_name = ""
-        self.master_host_name = ""
-        self.dirman_password = ""
-        self.host_name = ""
-        self.dir = ""
-        self.subject_base = ""
-
 def parse_options():
     usage = "%prog [options] REPLICA_FILE"
     parser = IPAOptionParser(usage=usage, version=version.VERSION)
@@ -76,6 +68,8 @@ def parse_options():
                       default=True, help="disables pkinit setup steps")
     parser.add_option("--skip-conncheck", dest="skip_conncheck", action="store_true",
                       default=False, help="skip connection check to remote master")
+    parser.add_option("--setup-ca", dest="setup_ca", action="store_true",
+                      default=False, help="configure a dogtag CA")
     parser.add_option("-U", "--unattended", dest="unattended", action="store_true",
                       default=False, help="unattended installation never prompts the user")
 
@@ -102,98 +96,10 @@ def parse_options():
 def get_dirman_password():
     return installutils.read_password("Directory Manager (existing master)", confirm=False, validate=False)
 
-def expand_info(filename, password):
-    top_dir = tempfile.mkdtemp("ipa")
-    tarfile = top_dir+"/files.tar"
-    dir = top_dir + "/realm_info"
-    ipautil.decrypt_file(filename, tarfile, password, top_dir)
-    ipautil.run(["tar", "xf", tarfile, "-C", top_dir])
-    os.remove(tarfile)
-
-    return top_dir, dir
-
-def read_info(dir, rconfig):
-    filename = dir + "/realm_info"
-    fd = open(filename)
-    config = SafeConfigParser()
-    config.readfp(fd)
-
-    rconfig.realm_name = config.get("realm", "realm_name")
-    rconfig.master_host_name = config.get("realm", "master_host_name")
-    rconfig.domain_name = config.get("realm", "domain_name")
-    rconfig.host_name = config.get("realm", "destination_host")
-    rconfig.subject_base = config.get("realm", "subject_base")
-
-def get_host_name(no_host_dns):
-    hostname = installutils.get_fqdn()
-    try:
-        installutils.verify_fqdn(hostname, no_host_dns)
-    except RuntimeError, e:
-        logging.error(str(e))
-        sys.exit(1)
-
-    return hostname
-
 def set_owner(config, dir):
     pw = pwd.getpwnam(dsinstance.DS_USER)
     os.chown(dir, pw.pw_uid, pw.pw_gid)
 
-def install_ca(config):
-    # FIXME, need to pass along the CA plugin to use
-    cafile = config.dir + "/cacert.p12"
-
-    if not ipautil.file_exists(cafile):
-        # CA not used on the server, return empty instances
-        return (None, None)
-
-    try:
-        from ipaserver.install import cainstance
-    except ImportError:
-        print >> sys.stderr, "Import failed: %s" % sys.exc_value
-        sys.exit(1)
-
-    if not cainstance.check_inst():
-        print "A CA was specified but the dogtag certificate server"
-        print "is not installed on the system"
-        print "Please install dogtag and restart the setup program"
-        sys.exit(1)
-
-    pkcs12_info = None
-    if ipautil.file_exists(config.dir + "/dogtagcert.p12"):
-        pkcs12_info = (config.dir + "/dogtagcert.p12",
-                       config.dir + "/dirsrv_pin.txt")
-    cs = cainstance.CADSInstance()
-    cs.create_instance(config.realm_name, config.host_name,
-                       config.domain_name, config.dirman_password,
-                       pkcs12_info)
-    cs.load_pkcs12()
-    cs.enable_ssl()
-    cs.restart_instance()
-    ca = cainstance.CAInstance(config.realm_name, certs.NSS_DIR)
-    ca.configure_instance(config.host_name, config.dirman_password,
-                          config.dirman_password, pkcs12_info=(cafile,),
-                          master_host=config.master_host_name,
-                          subject_base=config.subject_base)
-
-    # The dogtag DS instance needs to be restarted after installation.
-    # The procedure for this is: stop dogtag, stop DS, start DS, start
-    # dogtag
-    #
-    # The service_name trickery is due to the service naming we do
-    # internally. In the case of the dogtag DS the name doesn't match the
-    # unix service.
-
-    service_name = cs.service_name
-    service.print_msg("Restarting the directory and certificate servers")
-    cs.service_name = "dirsrv"
-    ca.stop()
-    cs.stop("PKI-IPA")
-    cs.start("PKI-IPA")
-    ca.start()
-    cs.service_name = service_name
-
-    return (ca, cs)
-
 def install_replica_ds(config):
     dsinstance.check_existing_installation()
     dsinstance.check_ports()
@@ -392,7 +298,7 @@ def main():
             sys.exit(0)
 
     try:
-        top_dir, dir = expand_info(filename, dirman_password)
+        top_dir, dir = expand_replica_info(filename, dirman_password)
         global REPLICA_INFO_TOP_DIR
         REPLICA_INFO_TOP_DIR = top_dir
     except Exception, e:
@@ -401,9 +307,13 @@ def main():
         sys.exit(1)
 
     config = ReplicaConfig()
-    read_info(dir, config)
+    read_replica_info(dir, config)
     config.dirman_password = dirman_password
-    host = get_host_name(options.no_host_dns)
+    try:
+        host = get_host_name(options.no_host_dns)
+    except RuntimeError, e:
+        logging.error(str(e))
+        sys.exit(1)
     if config.host_name != host:
         try:
             print "This replica was created for '%s' but this machine is named '%s'" % (config.host_name, host)
@@ -414,32 +324,12 @@ def main():
         except KeyboardInterrupt:
             sys.exit(0)
     config.dir = dir
+    config.setup_ca = options.setup_ca
 
 
     # check connection
     if not options.skip_conncheck:
-        print "Run connection check to master"
-        args = ["/usr/sbin/ipa-replica-conncheck", "--master", config.master_host_name,
-                "--auto-master-check", "--realm", config.realm_name,
-                "--principal", "admin",
-                "--hostname", config.host_name]
-
-        if options.admin_password:
-            args.extend(["--password", options.admin_password])
-
-        cafile = config.dir + "/cacert.p12"
-        if ipautil.file_exists(cafile): # with CA
-            args.append('--check-ca')
-        logging.debug("Running ipa-replica-conncheck with following arguments: %s" %
-                " ".join(args))
-        (stdin, stderr, returncode) = ipautil.run(args,raiseonerr=False, capture_output=False)
-
-        if returncode != 0:
-            sys.exit("Connection check failed!" +
-                     "\nPlease fix your network settings according to error messages above." +
-                     "\nIf the check results are not valid it can be skipped with --skip-conncheck parameter.")
-        else:
-            print "Connection check OK"
+        replica_conn_check(config.master_host_name, config.host_name, config.realm_name, options.setup_ca, options.admin_password)
 
     # Create the management framework config file
     # Note: We must do this before bootstraping and finalizing ipalib.api
@@ -511,7 +401,7 @@ def main():
         ntp.create_instance()
 
     # Configure the CA if necessary
-    (CA, cs) = install_ca(config)
+    (CA, cs) = cainstance.install_replica_ca(config)
 
     # Always try to install DNS records
     install_dns_records(config, options)
@@ -520,7 +410,7 @@ def main():
     ds = install_replica_ds(config)
 
     # We need to ldap_enable the CA now that DS is up and running
-    if CA:
+    if CA and config.setup_ca:
         CA.ldap_enable('CA', config.host_name, config.dirman_password,
                        util.realm_to_suffix(config.realm_name))
         cs.add_simple_service('dogtagldap/%s@%s' % (config.host_name, config.realm_name))
@@ -532,8 +422,9 @@ def main():
         CA.import_ra_cert(dir + "/ra.p12")
         CA.fix_ra_perms()
         service.restart("httpd")
-        service.print_msg("Setting the certificate subject base")
-        CA.set_subject_in_config(util.realm_to_suffix(config.realm_name))
+        if config.setup_ca:
+            service.print_msg("Setting the certificate subject base")
+            CA.set_subject_in_config(util.realm_to_suffix(config.realm_name))
 
     # The DS instance is created before the keytab, add the SSL cert we
     # generated
diff --git a/install/tools/man/Makefile.am b/install/tools/man/Makefile.am
index be2524e865fa89165a3023eb2d3c04043d694165..63a598ac2a1297c03a308a5efc9388329167a5b5 100644
--- a/install/tools/man/Makefile.am
+++ b/install/tools/man/Makefile.am
@@ -12,6 +12,7 @@ man1_MANS = 				\
 	ipa-server-certinstall.1	\
 	ipa-server-install.1		\
 	ipa-dns-install.1		\
+	ipa-ca-install.1		\
 	ipa-ldap-updater.1		\
 	ipa-compat-manage.1		\
 	ipa-nis-manage.1		\
diff --git a/install/tools/man/ipa-ca-install.1 b/install/tools/man/ipa-ca-install.1
new file mode 100644
index 0000000000000000000000000000000000000000..96d910ebb9c13ee696252984bea0490488f28ae2
--- /dev/null
+++ b/install/tools/man/ipa-ca-install.1
@@ -0,0 +1,49 @@
+.\" A man page for ipa-replica-install
+.\" Copyright (C) 2011 Red Hat, Inc.
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation, either version 3 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful, but
+.\" WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+.\" General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" along with this program.  If not, see <http://www.gnu.org/licenses/>.
+.\"
+.\" Author: Rob Crittenden <rcrit...@redhat.com>
+.\"
+.TH "ipa-ca-install" "1" "Jun 17 2011" "freeipa" ""
+.SH "NAME"
+ipa\-ca\-install \- Install a CA on a replica
+.SH "SYNOPSIS"
+ipa\-ca\-install [\fIOPTION\fR]... replica_file
+.SH "DESCRIPTION"
+Adds a CA as an IPA\-managed service. This requires that the IPA server is already installed and configured.
+
+The replica_file is created using the ipa\-replica\-prepare utility and should be the same one used when originally installing the replica.
+.SH "OPTIONS"
+\fB\-d\fR, \fB\-\-debug
+Enable debug logging when more verbose output is needed
+.TP
+\fB\-p\fR \fIDM_PASSWORD\fR, \fB\-\-password\fR=\fIDM_PASSWORD\fR
+Directory Manager (existing master) password
+.TP
+\fB\-w\fR \fIADMIN_PASSWORD\fR, \fB\-\-admin\-password\fR=\fIADMIN_PASSWORD\fR
+Admin user Kerberos password used for connection check
+.TP
+\fB\-\-no\-host\-dns\fR
+Do not use DNS for hostname lookup during installation
+.TP
+\fB\-\-skip\-conncheck\fR
+Skip connection check to remote master
+.TP
+\fB\-U\fR, \fB\-\-unattended\fR
+An unattended installation that will never prompt for user input
+.SH "EXIT STATUS"
+0 if the command was successful
+
+1 if an error occurred
diff --git a/install/tools/man/ipa-replica-install.1 b/install/tools/man/ipa-replica-install.1
index 8889235462ef52327c951714ab62982b5003c8cc..d259cdbdc0b6ea5f5ed70b1e5959301636cee077 100644
--- a/install/tools/man/ipa-replica-install.1
+++ b/install/tools/man/ipa-replica-install.1
@@ -33,7 +33,7 @@ Do not configure NTP
 \fB\-d\fR, \fB\-\-debug
 Enable debug logging when more verbose output is needed
 .TP
-\fB\-p\fR, \fB\-\-password\fR=\fIDM_PASSWORD\fR
+\fB\-p\fR \fIDM_PASSWORD\fR, \fB\-\-password\fR=\fIDM_PASSWORD\fR
 Directory Manager (existing master) password
 .TP
 \fB\-w\fR \fIADMIN_PASSWORD\fR, \fB\-\-admin\-password\fR=\fIADMIN_PASSWORD\fR
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
index 001e6eb09a7ab1980ace5a9cfcb7f00b836a15d9..209d810751711109f0328b9c52365b0b62cf89c1 100644
--- a/ipaserver/install/cainstance.py
+++ b/ipaserver/install/cainstance.py
@@ -52,6 +52,7 @@ from ipaserver.install import service
 from ipaserver.install import installutils
 from ipaserver.install import dsinstance
 from ipaserver.install import certs
+from ipaserver.install.installutils import ReplicaConfig
 from ipalib import util
 
 DEFAULT_DSPORT=7389
@@ -446,6 +447,7 @@ class CAInstance(service.Service):
         self.csr_file = None
         self.cert_file = None
         self.cert_chain_file = None
+        self.create_ra_agent_db = True
 
         # The same database is used for mod_nss because the NSS context
         # will already have been initialized by Apache by the time
@@ -522,7 +524,8 @@ class CAInstance(service.Service):
             self.step("restarting certificate server", self.__restart_instance)
             if not self.clone:
                 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)
+            if self.create_ra_agent_db:
+                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)
             if not self.clone:
                 self.step("restarting certificate server", self.__restart_instance)
@@ -1092,6 +1095,79 @@ class CAInstance(service.Service):
         fd.close()
         os.chmod(location, 0444)
 
+def install_replica_ca(config, postinstall=False):
+    """
+    Install a CA on a replica.
+
+    There are two modes of doing this controlled:
+      - While the replica is being installed
+      - Post-replica installation
+
+    config is a ReplicaConfig object
+
+    Returns a tuple of the CA and CADS instances
+    """
+    cafile = config.dir + "/cacert.p12"
+
+    if not ipautil.file_exists(cafile):
+        # not a dogtag CA replica
+        sys.exit('Not a dogtag CA installation')
+
+    if not config.setup_ca:
+        # We aren't configuring the CA in this step but we still need
+        # a minimum amount of information on the CA for this IPA install.
+        ca = CAInstance(config.realm_name, certs.NSS_DIR)
+        ca.dm_password = config.dirman_password
+        ca.subject_base = config.subject_base
+        return (ca, None)
+
+    ca = CAInstance(config.realm_name, certs.NSS_DIR)
+    ca.dm_password = config.dirman_password
+    ca.subject_base = config.subject_base
+    if ca.is_installed():
+        sys.exit("A CA is already configured on this system.")
+
+    pkcs12_info = None
+    if ipautil.file_exists(config.dir + "/dogtagcert.p12"):
+        pkcs12_info = (config.dir + "/dogtagcert.p12",
+                       config.dir + "/dirsrv_pin.txt")
+    cs = CADSInstance()
+    cs.create_instance(config.realm_name, config.host_name,
+                       config.domain_name, config.dirman_password,
+                       pkcs12_info)
+    cs.load_pkcs12()
+    cs.enable_ssl()
+    cs.restart_instance()
+    ca = CAInstance(config.realm_name, certs.NSS_DIR)
+    if postinstall:
+        # If installing this afterward the Apache NSS database already
+        # exists, don't remove it.
+        ca.create_ra_agent_db = False
+    ca.configure_instance(config.host_name, config.dirman_password,
+                          config.dirman_password, pkcs12_info=(cafile,),
+                          master_host=config.master_host_name,
+                          subject_base=config.subject_base)
+
+    # The dogtag DS instance needs to be restarted after installation.
+    # The procedure for this is: stop dogtag, stop DS, start DS, start
+    # dogtag
+    #
+    #
+    # The service_name trickery is due to the service naming we do
+    # internally. In the case of the dogtag DS the name doesn't match the
+    # unix service.
+
+    service_name = cs.service_name
+    service.print_msg("Restarting the directory and certificate servers")
+    cs.service_name = "dirsrv"
+    ca.stop()
+    cs.stop("PKI-IPA")
+    cs.start("PKI-IPA")
+    ca.start()
+    cs.service_name = service_name
+
+    return (ca, cs)
+
 if __name__ == "__main__":
     installutils.standard_logging_setup("install.log", False)
     cs = CADSInstance()
diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py
index 07dda2cc0ff9e62ec4aa761d6fabfa6254549db6..ebe654dd30379ff709c69574deae438f84b6e27c 100644
--- a/ipaserver/install/certs.py
+++ b/ipaserver/install/certs.py
@@ -446,6 +446,7 @@ class CertDB(object):
                 return cert
             else:
                 (cert, start) = find_cert_from_txt(cert, start=0)
+                cert = x509.strip_header(cert)
                 dercert = base64.b64decode(cert)
                 return dercert
         except ipautil.CalledProcessError:
@@ -475,7 +476,8 @@ class CertDB(object):
 
         service.stop("certmonger")
         cert = self.get_cert_from_db(nickname)
-        subject = str(x509.get_subject(cert))
+        nsscert = x509.load_certificate(cert, dbdir=self.secdir)
+        subject = str(nsscert.subject)
         m = re.match('New tracking request "(\d+)" added', stdout)
         if not m:
             logging.error('Didn\'t get new certmonger request, got %s' % stdout)
diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py
index f5a862599dd6b41e28ef8b4f04c2ea09d0102ec4..68fce7e69bc94858f8484a2ebfb01eaefd3e88d2 100644
--- a/ipaserver/install/installutils.py
+++ b/ipaserver/install/installutils.py
@@ -29,6 +29,8 @@ import struct
 import fcntl
 import netaddr
 import time
+import tempfile
+from ConfigParser import SafeConfigParser
 
 from ipapython import ipautil
 from ipapython import dnsclient
@@ -36,6 +38,17 @@ from ipapython import dnsclient
 class HostnameLocalhost(Exception):
     pass
 
+class ReplicaConfig:
+    def __init__(self):
+        self.realm_name = ""
+        self.domain_name = ""
+        self.master_host_name = ""
+        self.dirman_password = ""
+        self.host_name = ""
+        self.dir = ""
+        self.subject_base = ""
+        self.setup_ca = False
+
 def get_fqdn():
     fqdn = ""
     try:
@@ -442,3 +455,47 @@ def resolve_host(host_name):
         return addrinfos[0][4][0]
     except:
         return None
+
+def get_host_name(no_host_dns):
+    """
+    Get the current FQDN from the socket and verify that it is valid.
+
+    no_host_dns is a boolean that determines whether we enforce that the
+    hostname is resolvable.
+
+    Will raise a RuntimeError on error, returns hostname on success
+    """
+    hostname = get_fqdn()
+    verify_fqdn(hostname, no_host_dns)
+    return hostname
+
+def expand_replica_info(filename, password):
+    """
+    Decrypt and expand a replica installation file into a temporary
+    location. The caller is responsible to remove this directory.
+    """
+    top_dir = tempfile.mkdtemp("ipa")
+    tarfile = top_dir+"/files.tar"
+    dir = top_dir + "/realm_info"
+    ipautil.decrypt_file(filename, tarfile, password, top_dir)
+    ipautil.run(["tar", "xf", tarfile, "-C", top_dir])
+    os.remove(tarfile)
+
+    return top_dir, dir
+
+def read_replica_info(dir, rconfig):
+    """
+    Read the contents of a replica installation file.
+
+    rconfig is a ReplicaConfig object
+    """
+    filename = dir + "/realm_info"
+    fd = open(filename)
+    config = SafeConfigParser()
+    config.readfp(fd)
+
+    rconfig.realm_name = config.get("realm", "realm_name")
+    rconfig.master_host_name = config.get("realm", "master_host_name")
+    rconfig.domain_name = config.get("realm", "domain_name")
+    rconfig.host_name = config.get("realm", "destination_host")
+    rconfig.subject_base = config.get("realm", "subject_base")
diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py
index e640873baf0143217bc634c6ece41c12d7aca819..fddb7374736906ad595eace32982cbe7102dad90 100644
--- a/ipaserver/install/replication.py
+++ b/ipaserver/install/replication.py
@@ -20,6 +20,7 @@
 import time, logging
 
 import os
+import sys
 import ldap
 from ipaserver import ipaldap
 from ipaserver.install.service import restart
@@ -27,6 +28,7 @@ import installutils
 from ldap import modlist
 from ipalib import util
 from ipalib import errors
+from ipapython import ipautil
 
 DIRMAN_CN = "cn=directory manager"
 CACERT = "/etc/ipa/ca.crt"
@@ -40,6 +42,37 @@ TIMEOUT = 120
 IPA_REPLICA = 1
 WINSYNC = 2
 
+def replica_conn_check(master_host, host_name, realm, check_ca,
+                       admin_password=None):
+    """
+    Check the ports used by the replica both locally and remotely to be sure
+    that replication will work.
+
+    Does not return a value, will sys.exit() on failure.
+    """
+    print "Run connection check to master"
+    args = ["/usr/sbin/ipa-replica-conncheck", "--master", master_host,
+            "--auto-master-check", "--realm", realm,
+            "--principal", "admin",
+            "--hostname", host_name]
+
+    if admin_password:
+        args.extend(["--password", admin_password])
+
+    if check_ca:
+        args.append('--check-ca')
+    logging.debug("Running ipa-replica-conncheck with following arguments: %s" %
+            " ".join(args))
+    (stdin, stderr, returncode) = ipautil.run(args,raiseonerr=False, capture_output=False)
+
+    if returncode != 0:
+        sys.exit("Connection check failed!" +
+                 "\nPlease fix your network settings according to error messages above." +
+                 "\nIf the check results are not valid it can be skipped with --skip-conncheck parameter.")
+    else:
+        print "Connection check OK"
+
+
 def check_replication_plugin():
     """
     Confirm that the 389-ds replication is installed.
-- 
1.7.4

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to