From ae655c0425cb8e921dbe24752e1af768840b962b Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Fri, 30 Sep 2011 17:53:35 -0400 Subject: install: Add tool to convert from selfsign to dogtag CA --- install/tools/ipa-ca-convert | 305 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 305 insertions(+) create mode 100755 install/tools/ipa-ca-convert 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 +# Rob Crittenden +# +# 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 . +# + +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=)") + 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) -- cgit