From ac0fb8ea524dcc86f358f405ba233c8dcbef71ed Mon Sep 17 00:00:00 2001 From: Karl MacMillan Date: Thu, 20 Dec 2007 17:05:16 -0500 Subject: Convert replication to use the new cert infrastructure and correctly issue certs from the same authority. Also remove support for read-only replicas since that work will not be finished and tested for 1.0. --- ipa-server/ipa-install/ipa-replica-install | 45 +++++++++++++++++++++--------- ipa-server/ipa-install/ipa-replica-prepare | 44 ++++++++++++++--------------- ipa-server/ipaserver/certs.py | 29 +++++++++++++++++-- ipa-server/ipaserver/dsinstance.py | 25 +++++++++-------- ipa-server/ipaserver/ipaldap.py | 15 ++++++---- ipa-server/ipaserver/krbinstance.py | 2 +- ipa-server/ipaserver/replication.py | 25 +++++++---------- 7 files changed, 114 insertions(+), 71 deletions(-) diff --git a/ipa-server/ipa-install/ipa-replica-install b/ipa-server/ipa-install/ipa-replica-install index 706dc323d..5d5eaeaed 100644 --- a/ipa-server/ipa-install/ipa-replica-install +++ b/ipa-server/ipa-install/ipa-replica-install @@ -21,13 +21,13 @@ import sys sys.path.append("/usr/share/ipa") -import tempfile +import tempfile, os, pwd, traceback, logging from ConfigParser import SafeConfigParser from ipa import ipautil from ipaserver import dsinstance, replication, installutils, krbinstance, service -from ipaserver import httpinstance, webguiinstance, radiusinstance, ntpinstance +from ipaserver import httpinstance, webguiinstance, radiusinstance, ntpinstance, certs class ReplicaConfig: def __init__(self): @@ -42,8 +42,8 @@ class ReplicaConfig: def parse_options(): from optparse import OptionParser parser = OptionParser() - parser.add_option("-r", "--read-only", dest="master", action="store_false", - default=True, help="create read-only replica - default is master") + parser.add_option("-d", "--debug", dest="debug", action="store_true", + default=False, help="gather extra debugging information") options, args = parser.parse_args() @@ -82,12 +82,25 @@ def get_host_name(): return hostname +def set_owner(config, dir): + pw = pwd.getpwnam(config.ds_user) + os.chown(dir, pw.pw_uid, pw.pw_gid) + def install_ds(config): dsinstance.check_existing_installation() dsinstance.check_ports() + # if we have a pkcs12 file, create the cert db from + # that. Otherwise the ds setup will create the CA + # cert + pkcs12_info = None + if ipautil.file_exists(config.dir + "/cacert.p12"): + pkcs12_info = (config.dir + "/cacert.p12", + config.dir + "/pwdfile.txt") + ds = dsinstance.DsInstance() - ds.create_instance(config.ds_user, config.realm_name, config.host_name, config.dirman_password) + ds.create_instance(config.ds_user, config.realm_name, config.host_name, config.dirman_password, + pkcs12_info) def install_krb(config): krb = krbinstance.KrbInstance() @@ -101,6 +114,8 @@ def install_http(config): def main(): options, filename = parse_options() + installutils.standard_logging_setup("ipareplica-install.log", options.debug) + top_dir, dir = expand_info(filename) config = ReplicaConfig() @@ -115,7 +130,9 @@ def main(): install_ds(config) repl = replication.ReplicationManager(config.host_name, config.dirman_password) - repl.setup_replication(config.master_host_name, config.realm_name, options.master) + ret = repl.setup_replication(config.master_host_name, config.realm_name) + if ret != 0: + raise RuntimeError("failed to start replication") install_krb(config) install_http(config) @@ -124,11 +141,6 @@ def main(): webgui = webguiinstance.WebGuiInstance() webgui.create_instance() - # Create a radius instance - radius = radiusinstance.RadiusInstance() - # FIXME: ldap_server should be derived, not hardcoded to localhost, also should it be a URL? - radius.create_instance(config.realm_name, config.host_name, 'localhost') - # Configure ntpd ntp = ntpinstance.NTPInstance() ntp.create_instance() @@ -137,6 +149,13 @@ def main(): service.restart("dirsrv") service.restart("krb5kdc") -main() - +try: + main() +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) + sys.exit(1) diff --git a/ipa-server/ipa-install/ipa-replica-prepare b/ipa-server/ipa-install/ipa-replica-prepare index 705c731d8..2f1e0853c 100644 --- a/ipa-server/ipa-install/ipa-replica-prepare +++ b/ipa-server/ipa-install/ipa-replica-prepare @@ -26,10 +26,7 @@ from ConfigParser import SafeConfigParser import krbV from ipa import ipautil -from ipaserver import dsinstance -from ipaserver import installutils - -certutil = "/usr/bin/certutil" +from ipaserver import dsinstance, installutils, certs def get_host_name(): hostname = installutils.get_fqdn() @@ -51,18 +48,25 @@ def check_ipa_configuration(realm_name): logging.error("could not find directory instance: %s" % config_dir) sys.exit(1) -def create_certdb(ds_dir, dir): - # copy the passwd, noise, and pin files - shutil.copyfile(ds_dir + "/pwdfile.txt", dir + "/pwdfile.txt") - shutil.copyfile(ds_dir + "/noise.txt", dir + "/noise.txt") - shutil.copyfile(ds_dir + "/pin.txt", dir + "/pin.txt") +def export_certdb(ds_dir, dir): + ds_cdb = certs.CertDB(ds_dir) + + pkcs12_fname = dir + "/cacert.p12" + passwd_fname = dir + "/pwdfile.txt" + fd = open(passwd_fname, "w") + fd.write("\n") + fd.close() - # create a new cert db - ipautil.run([certutil, "-N", "-d", dir, "-f", dir + "/pwdfile.txt"]) + try: + ds_cdb.export_pkcs12(pkcs12_fname, passwd_fname) + except ipautil.CalledProcessError, e: + print "error exporting CA certificate: " + str(e) + try: + os.unlink(pkcs12_fname) + os.unlink(passwd_fname) + except: + pass - # Add the CA cert - ipautil.run([certutil, "-A", "-d", dir, "-n", "CA certificate", "-t", "CT,CT", "-a", "-i", - ds_dir + "/cacert.asc"]) def get_ds_user(ds_dir): uid = os.stat(ds_dir).st_uid @@ -70,10 +74,6 @@ def get_ds_user(ds_dir): return user -def copy_files(realm_name, dir): - shutil.copy("/var/kerberos/krb5kdc/ldappwd", dir + "/ldappwd") - - def save_config(dir, realm_name, host_name, ds_user): config = SafeConfigParser() config.add_section("realm") @@ -82,7 +82,9 @@ def save_config(dir, realm_name, host_name, ds_user): config.set("realm", "ds_user", ds_user) fd = open(dir + "/realm_info", "w") config.write(fd) - + +def copy_files(realm_name, dir): + shutil.copy("/var/kerberos/krb5kdc/ldappwd", dir + "/ldappwd") def main(): realm_name = get_realm_name() @@ -96,10 +98,8 @@ def main(): dir = top_dir + "/realm_info" os.mkdir(dir, 0700) - create_certdb(ds_dir, dir) - + export_certdb(ds_dir, dir) copy_files(realm_name, dir) - save_config(dir, realm_name, host_name, ds_user) ipautil.run(["/bin/tar", "cfz", "replica-info-" + realm_name, "-C", top_dir, "realm_info"]) diff --git a/ipa-server/ipaserver/certs.py b/ipa-server/ipaserver/certs.py index eecfdf21c..08f2cdd6c 100644 --- a/ipa-server/ipaserver/certs.py +++ b/ipa-server/ipaserver/certs.py @@ -119,6 +119,7 @@ class CertDB(object): "-z", self.noise_fname, "-f", self.passwd_fname]) + def export_ca_cert(self): # export the CA cert for use with other apps ipautil.backup_file(self.cacert_fname) self.run_certutil(["-L", "-n", "CA certificate", @@ -274,21 +275,33 @@ class CertDB(object): return server_certs - def import_pkcs12(self, pkcs12_fname): + def import_pkcs12(self, pkcs12_fname, passwd_fname=None): + args = ["/usr/bin/pk12util", "-d", self.secdir, + "-i", pkcs12_fname, + "-k", self.passwd_fname] + if passwd_fname: + args = args + ["-w", passwd_fname] try: - ipautil.run(["/usr/bin/pk12util", "-d", self.secdir, - "-i", pkcs12_fname]) + ipautil.run(args) except ipautil.CalledProcessError, e: if e.returncode == 17: raise RuntimeError("incorrect password") else: raise RuntimeError("unknown error import pkcs#12 file") + def export_pkcs12(self, pkcs12_fname, pkcs12_pwd_fname, nickname="CA certificate"): + ipautil.run(["/usr/bin/pk12util", "-d", self.secdir, + "-o", pkcs12_fname, + "-n", nickname, + "-k", self.passwd_fname, + "-w", pkcs12_pwd_fname]) + def create_self_signed(self, passwd=True): self.create_noise_file() self.create_passwd_file(passwd) self.create_certdbs() self.create_ca_cert() + self.export_ca_cert() self.create_pin_file() def create_from_cacert(self, cacert_fname, passwd=False): @@ -296,3 +309,13 @@ class CertDB(object): self.create_passwd_file(passwd) self.create_certdbs() self.load_cacert(cacert_fname) + + def create_from_pkcs12(self, pkcs12_fname, pkcs12_pwd_fname, nickname="CA certificate", passwd=True): + self.create_noise_file() + self.create_passwd_file(passwd) + self.create_certdbs() + self.import_pkcs12(pkcs12_fname, pkcs12_pwd_fname) + self.trust_root_cert(nickname) + self.create_pin_file() + self.export_ca_cert() + diff --git a/ipa-server/ipaserver/dsinstance.py b/ipa-server/ipaserver/dsinstance.py index 6cbffcb84..e3c8cb8d4 100644 --- a/ipa-server/ipaserver/dsinstance.py +++ b/ipa-server/ipaserver/dsinstance.py @@ -125,7 +125,7 @@ class DsInstance(service.Service): self.sub_dict = None self.domain = None - def create_instance(self, ds_user, realm_name, host_name, dm_password, ro_replica=False): + def create_instance(self, ds_user, realm_name, host_name, dm_password, pkcs12_info=None): self.ds_user = ds_user self.realm_name = realm_name.upper() self.serverid = realm_to_serverid(self.realm_name) @@ -133,13 +133,13 @@ class DsInstance(service.Service): self.host_name = host_name self.dm_password = dm_password self.domain = host_name[host_name.find(".")+1:] + self.pkcs12_info = pkcs12_info self.__setup_sub_dict() self.step("creating directory server user", self.__create_ds_user) self.step("creating directory server instance", self.__create_instance) self.step("adding default schema", self.__add_default_schemas) - if not ro_replica: - self.step("enabling memberof plugin", self.__add_memberof_module) + self.step("enabling memberof plugin", self.__add_memberof_module) self.step("enabling referential integrity plugin", self.__add_referint_module) self.step("enabling distributed numeric assignment plugin", self.__add_dna_module) self.step("creating indeces", self.__create_indeces) @@ -147,13 +147,12 @@ class DsInstance(service.Service): self.step("configuring certmap.conf", self.__certmap_conf) self.step("restarting directory server", self.__restart_instance) self.step("adding default layout", self.__add_default_layout) - if not ro_replica: - self.step("configuring Posix uid/gid generation as first master", - self.__config_uidgid_gen_first_master) - self.step("adding master entry as first master", - self.__add_master_entry_first_master) - self.step("initializing group membership", - self.__init_memberof) + self.step("configuring Posix uid/gid generation as first master", + self.__config_uidgid_gen_first_master) + self.step("adding master entry as first master", + self.__add_master_entry_first_master) + self.step("initializing group membership", + self.__init_memberof) self.step("configuring directory to start on boot", self.chkconfig_on) @@ -261,7 +260,11 @@ class DsInstance(service.Service): def __enable_ssl(self): dirname = config_dirname(self.realm_name) ca = certs.CertDB(dirname) - ca.create_self_signed() + if self.pkcs12_info: + ca.create_from_pkcs12(self.pkcs12_info[0], self.pkcs12_info[1]) + ca.cur_serial = 2000 + else: + ca.create_self_signed() ca.create_server_cert("Server-Cert", "cn=%s,ou=Fedora Directory Server" % self.host_name) conn = ipaldap.IPAdmin("127.0.0.1") diff --git a/ipa-server/ipaserver/ipaldap.py b/ipa-server/ipaserver/ipaldap.py index b1a9ea56c..7bb9719ea 100644 --- a/ipa-server/ipaserver/ipaldap.py +++ b/ipa-server/ipaserver/ipaldap.py @@ -724,9 +724,12 @@ def notfound(args): search returns no results. This just returns whatever is after the equals sign""" - filter = args[2] - try: - target = re.match(r'\(.*=(.*)\)', filter).group(1) - except: - target = filter - return "%s not found" % str(target) + if len(args) > 2: + filter = args[2] + try: + target = re.match(r'\(.*=(.*)\)', filter).group(1) + except: + target = filter + return "%s not found" % str(target) + else: + return args[0] diff --git a/ipa-server/ipaserver/krbinstance.py b/ipa-server/ipaserver/krbinstance.py index 5c4976b72..438bfb7d5 100644 --- a/ipa-server/ipaserver/krbinstance.py +++ b/ipa-server/ipaserver/krbinstance.py @@ -257,7 +257,7 @@ class KrbInstance(service.Service): self.__ldap_mod("default-aci.ldif") def __create_replica_instance(self): - self.__create_instance(replace=True) + self.__create_instance(replica=True) def __create_instance(self, replica=False): kdc_conf = ipautil.template_file(ipautil.SHARE_DIR+"kdc.conf.template", self.sub_dict) diff --git a/ipa-server/ipaserver/replication.py b/ipa-server/ipaserver/replication.py index 580ec27bf..df2b02889 100644 --- a/ipa-server/ipaserver/replication.py +++ b/ipa-server/ipaserver/replication.py @@ -77,7 +77,7 @@ class ReplicationManager: except ldap.NO_SUCH_OBJECT: pass - def get_replica_type(self, master): + def get_replica_type(self, master=True): if master: return "3" else: @@ -87,7 +87,7 @@ class ReplicationManager: return 'cn=replica, cn="%s", cn=mapping tree, cn=config' % self.suffix - def local_replica_config(self, conn, master, replica_id): + def local_replica_config(self, conn, replica_id): dn = self.replica_dn() try: @@ -97,7 +97,7 @@ class ReplicationManager: except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND): pass - replica_type = self.get_replica_type(master) + replica_type = self.get_replica_type() entry = ipaldap.Entry(dn) entry.setValues('objectclass', "top", "nsds5replica", "extensibleobject") @@ -284,13 +284,12 @@ class ReplicationManager: return self.wait_for_repl_init(other_conn, dn) - def basic_replication_setup(self, conn, master, replica_id): + def basic_replication_setup(self, conn, replica_id): self.add_replication_manager(conn) - self.local_replica_config(conn, master, replica_id) - if master: - self.setup_changelog(conn) + self.local_replica_config(conn, replica_id) + self.setup_changelog(conn) - def setup_replication(self, other_hostname, realm_name, master=True): + def setup_replication(self, other_hostname, realm_name): """ NOTES: - the directory manager password needs to be the same on @@ -300,15 +299,11 @@ class ReplicationManager: other_conn.do_simple_bind(bindpw=self.dirman_passwd) self.suffix = ipaldap.IPAdmin.normalizeDN(dsinstance.realm_to_suffix(realm_name)) - self.basic_replication_setup(self.conn, master, 1) - self.basic_replication_setup(other_conn, True, 2) + self.basic_replication_setup(self.conn, 1) + self.basic_replication_setup(other_conn, 2) self.setup_agreement(other_conn, self.conn) - if master: - self.setup_agreement(self.conn, other_conn) - else: - self.setup_chaining_farm(other_conn) - self.setup_chain_on_update(other_conn) + self.setup_agreement(self.conn, other_conn) return self.start_replication(other_conn) -- cgit