summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimo Sorce <ssorce@redhat.com>2011-09-30 17:53:35 -0400
committerSimo Sorce <ssorce@redhat.com>2011-10-12 16:46:36 -0400
commitae655c0425cb8e921dbe24752e1af768840b962b (patch)
treedfeedd6213851331681514780fe402a805d91b23
parentcdae256d0aa13451f028d5c3afb517beaed1e970 (diff)
downloadfreeipa-ae655c0425cb8e921dbe24752e1af768840b962b.tar.gz
freeipa-ae655c0425cb8e921dbe24752e1af768840b962b.tar.xz
freeipa-ae655c0425cb8e921dbe24752e1af768840b962b.zip
install: Add tool to convert from selfsign to dogtag CA
-rwxr-xr-xinstall/tools/ipa-ca-convert305
1 files changed, 305 insertions, 0 deletions
diff --git a/install/tools/ipa-ca-convert b/install/tools/ipa-ca-convert
new file mode 100755
index 000000000..80198470c
--- /dev/null
+++ b/install/tools/ipa-ca-convert
@@ -0,0 +1,305 @@
+#! /usr/bin/python -E
+# Authors: Simo Sorce <ssorce@redhat.com>
+# Rob Crittenden <rcritten@redhat.com>
+#
+# Copyright (C) 2007-2010 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 os
+import errno
+import logging
+import grp
+import subprocess
+import signal
+import shutil
+import glob
+import traceback
+from ConfigParser import RawConfigParser
+import random
+import tempfile
+import nss.error
+from optparse import OptionGroup
+
+from ipapython import version
+from ipapython.ipautil import user_input
+from ipaserver.install import certs
+from ipaserver.install import cainstance
+from ipaserver.install import dsinstance
+from ipaserver.install import httpinstance
+from ipaserver.install import service
+from ipaserver.install.installutils import *
+from ipaserver.plugins.ldap2 import ldap2
+from ipapython.config import IPAOptionParser
+from ipalib import api, errors, util
+from ipapython import certmonger
+from ipapython import services as ipaservices
+from ipalib.dn import DN
+
+VALID_SUBJECT_ATTRS = ['cn', 'st', 'o', 'ou', 'dnqualifier', 'c',
+ 'serialnumber', 'l', 'title', 'sn', 'givenname',
+ 'initials', 'generationqualifier', 'dc', 'mail',
+ 'uid', 'postaladdress', 'postalcode', 'postofficebox',
+ 'houseidentifier', 'e', 'street', 'pseudonym',
+ 'incorporationlocality', 'incorporationstate',
+ 'incorporationcountry', 'businesscategory']
+
+def subject_callback(option, opt_str, value, parser):
+ """
+ Make sure the certificate subject base is a valid DN
+ """
+ name = opt_str.replace('--','')
+ v = unicode(value, 'utf-8')
+ try:
+ dn = DN(v)
+ for rdn in dn:
+ if rdn.attr.lower() not in VALID_SUBJECT_ATTRS:
+ raise ValueError('invalid attribute: %s' % rdn.attr)
+ except ValueError, e:
+ raise ValueError('Invalid subject base format: %s' % str(e))
+ parser.values.subject = str(dn) # may as well normalize it
+
+def parse_options():
+ parser = IPAOptionParser(version=version.VERSION)
+
+ basic_group = OptionGroup(parser, "basic options")
+ basic_group.add_option("-p", "--ds-password", dest="dm_password",
+ sensitive=True, help="admin password")
+ basic_group.add_option("-d", "--debug", dest="debug", action="store_true",
+ default=False, help="print debugging information")
+ basic_group.add_option("-U", "--unattended", dest="unattended", action="store_true",
+ default=False, help="unattended (un)installation never prompts the user")
+ parser.add_option_group(basic_group)
+
+ cert_group = OptionGroup(parser, "certificate system options")
+ cert_group.add_option("--subject", action="callback", callback=subject_callback,
+ type="string",
+ help="The certificate subject base (default O=<realm-name>)")
+ parser.add_option_group(cert_group)
+
+ options, args = parser.parse_args()
+ safe_options = parser.get_safe_opts(options)
+
+ if options.unattended and not options.dm_password:
+ parser.error("In unattended mode you need to provide at least the DM password (-p)")
+
+ return safe_options, options
+
+def get_dirman_password():
+ return read_password("Directory Manager (existing master)", confirm=False, validate=False)
+
+def main():
+ safe_options, options = parse_options()
+
+ if os.getegid() != 0:
+ sys.exit("Must be root to set up server")
+
+ standard_logging_setup("/var/log/ipaserver-ca-convert.log", options.debug)
+ print "\nThe log file for this installation can be found in /var/log/ipaserver-ca-convert.log"
+ # TODO: check selfsign and fail if dogtag already installed
+ logging.debug('%s was invoked with options: %s' % (sys.argv[0], safe_options))
+ logging.debug("missing options might be asked for interactively later\n")
+
+ if not is_ipa_configured():
+ sys.exit("IPA server is not installed on this system.\n")
+
+ global fstore
+ fstore = sysrestore.FileStore('/var/lib/ipa/sysrestore')
+ global sstore
+ sstore = sysrestore.StateFile('/var/lib/ipa/sysrestore')
+
+ # Configuration for ipalib, we will bootstrap and finalize later, after
+ # we are sure we have the configuration file ready.
+ cfg = dict(
+ context='installer',
+ in_server=True,
+ debug=options.debug
+ )
+
+ api.bootstrap(**cfg)
+ api.finalize()
+
+ print "=============================================================================="
+ print "This program will convert the FreeIPA Server to use the Dogtag CA."
+ print ""
+ print "To accept the default shown in brackets, press the Enter key."
+ print ""
+
+ print ""
+ print "WARNING: this will DESTROY and REMOVE your selfsigned CA!"
+ print "This will require you to generate and distribute the new CA certificate and"
+ print "new service certificates to all clients and servers in your FreeIPA domain"
+ print ""
+ if not user_input("Are you sure you want to continue with this procedure?", False):
+ print ""
+ print "Aborting CA convertion operation."
+ sys.exit(1)
+
+ # Use str() to un-unicodify the strings or later on an ldap operation will
+ # fail due to buggy bindings not understanding unicode strings
+ host_name = str(api.env.host)
+ realm_name = str(api.env.realm)
+ domain_name = str(api.env.domain)
+
+ # get the directory manager password
+ dm_password = options.dm_password
+ if not options.dm_password:
+ try:
+ dm_password = get_dirman_password()
+ except KeyboardInterrupt:
+ sys.exit(0)
+
+ # Try out the password
+ try:
+ conn = ldap2(shared_instance=False)
+ conn.connect(bind_dn='cn=directory manager', bind_pw=dm_password)
+ conn.disconnect()
+ except errors.ACIError:
+ sys.exit("\nThe password provided is incorrect for LDAP server %s" % host_name)
+ except errors.LDAPError:
+ sys.exit("\nUnable to connect to LDAP server %s" % host_name)
+
+ # Update the management framework config file
+ target_fname = '/etc/ipa/default.conf'
+ fd = open(target_fname, "a+")
+ fd.write("ra_plugin=dogtag\n")
+ fd.close()
+ if not options.unattended:
+ print ""
+ print "The following operations may take some minutes to complete."
+ print "Please wait until the prompt is returned."
+ print ""
+
+ # Mess up with api, as we need to reflect changes made to default.conf
+ # after it has been already finalized
+ object.__setattr__(api.env, 'enable_ra', True)
+ object.__setattr__(api.env, 'ra_plugin', 'dogtag')
+
+ # Clean up any previous self-signed CA that may exist
+ try:
+ os.rename(certs.CA_SERIALNO, certs.CA_SERIALNO + '.selfsign')
+ except:
+ pass
+
+ cs = cainstance.CADSInstance(host_name, realm_name, domain_name, dm_password)
+ if not cs.is_configured():
+ cs.create_instance(realm_name, host_name, domain_name, dm_password, subject_base=options.subject)
+ ca = cainstance.CAInstance(realm_name, certs.NSS_DIR)
+ ca.configure_instance(host_name, dm_password, dm_password, subject_base=options.subject)
+
+ # Now put the CA cert where other instances exepct it
+ ca.publish_ca_cert("/etc/ipa/ca.crt")
+ ca.publish_ca_cert("/usr/share/ipa/html/ca.crt")
+
+ ca.ldap_enable('CA', host_name, dm_password, api.env.basedn)
+
+ # Turn on SSL in the dogtag LDAP instance. This will get restarted
+ # later, we don't need SSL now.
+ cs.create_certdb()
+ cs.enable_ssl()
+ # Add the IPA service for storing the PKI-IPA server certificate.
+ cs.add_simple_service(cs.principal)
+ cs.add_cert_to_service()
+
+ # TODO Create new DS, HTTPS and (if pkinit is configured) KDC certs
+
+ serverid = dsinstance.realm_to_serverid(realm_name)
+ dirname = dsinstance.config_dirname(serverid).rstrip('/ ')
+
+ print "Start certmonger if needed"
+ cmonger = ipaservices.knownservices.certmonger
+ ipaservices.knownservices.messagebus.start()
+ cmonger.start()
+
+ print "Untrack old certs if any"
+ #LDAP
+ try:
+ (o,e,i) = certmonger.stop_tracking(dirname, nickname="Server-Cert")
+ print (o, e, i)
+ except (ipautil.CalledProcessError, RuntimeError), e:
+ logging.error("certmonger failed to stop tracking certificate: %s" % str(e))
+ # HTTP
+ try:
+ (o,e,i) = certmonger.stop_tracking(certs.NSS_DIR, nickname="Server-Cert")
+ print (o, e, i)
+ except (ipautil.CalledProcessError, RuntimeError), e:
+ logging.error("certmonger failed to stop tracking certificate: %s" % str(e))
+
+ print "Generating new cert for LDAP"
+ dsdb = certs.CertDB(realm_name, nssdir=dirname,
+ subject_base=options.subject)
+ nickname = "Server-Cert"
+ cadb = certs.CertDB(realm_name, host_name=host_name,
+ subject_base=options.subject)
+ cadb.export_ca_cert('ipaCert', False)
+ dsdb.create_from_cacert(cadb.cacert_fname, passwd=None)
+ dercert = dsdb.create_server_cert("Server-Cert", host_name, cadb)
+ dsdb.track_server_cert("Server-Cert",
+ "ldap/%s@%s" % (host_name, realm_name),
+ dsdb.passwd_fname)
+ dsdb.create_pin_file()
+
+ print "Restarting the directory server"
+ ds = dsinstance.DsInstance(fstore=fstore)
+ ds.restart()
+
+ # ds.add_cert_to_service()
+
+ print "Generating new cert for HTTPS"
+ http = httpinstance.HTTPInstance(fstore)
+ http.fqdn = host_name
+ http.realm = realm_name
+ http.domain = domain_name
+ http.dm_password = dm_password
+ http.self_signed_ca = False
+ http.pkcs12_info = None
+ http.subject_base = options.subject
+ http._HTTPInstance__setup_ssl() #pylint: disable=E1101
+
+ print "Restarting the web server"
+ http.restart()
+
+ # http.add_cert_to_service()
+
+ print "=============================================================================="
+ print "Setup complete"
+ print ""
+ print "Be sure to back up the CA certificate stored in /root/cacert.p12"
+ print "This file is required to create replicas. The password for this"
+ print "file is the Directory Manager password"
+
+ return 0
+
+try:
+ sys.exit(main())
+except SystemExit, e:
+ sys.exit(e)
+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 "The KDC service does not listen on localhost"
+ print ""
+ print "Please fix your /etc/hosts file and restart the setup program"
+except Exception, e:
+ message = "Unexpected error - see ipaserver-ca-convert.log for details:\n %s" % str(e)
+ print message
+ message = str(e)
+ for str in traceback.format_tb(sys.exc_info()[2]):
+ message = message + "\n" + str
+ logging.debug(message)
+ sys.exit(1)