From 8a32bb3746802a29b2655e4ad2cbbba8481e1eaf Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Fri, 17 Jun 2011 16:47:39 -0400 Subject: 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 --- ipaserver/install/cainstance.py | 78 ++++++++++++++++++++++++++++++++++++++- ipaserver/install/certs.py | 4 +- ipaserver/install/installutils.py | 57 ++++++++++++++++++++++++++++ ipaserver/install/replication.py | 33 +++++++++++++++++ 4 files changed, 170 insertions(+), 2 deletions(-) (limited to 'ipaserver/install') diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py index 928d01e47..4ace26db5 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 @@ -521,7 +523,8 @@ class CAInstance(service.Service): if self.external != 1: 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) @@ -1091,6 +1094,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 07dda2cc0..ebe654dd3 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 f5a862599..68fce7e69 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 e640873ba..fddb73747 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. -- cgit