diff --git a/ipa-server/ipa-install/ipa-replica-install b/ipa-server/ipa-install/ipa-replica-install index 792e31bbc..57fae7fe6 100644 --- a/ipa-server/ipa-install/ipa-replica-install +++ b/ipa-server/ipa-install/ipa-replica-install @@ -20,13 +20,13 @@ import sys -import tempfile, os, pwd, traceback, logging +import tempfile, os, pwd, traceback, logging, shutil from ConfigParser import SafeConfigParser from ipa import ipautil from ipaserver import dsinstance, replication, installutils, krbinstance, service -from ipaserver import httpinstance, webguiinstance, ntpinstance, certs +from ipaserver import httpinstance, ntpinstance, certs class ReplicaConfig: def __init__(self): @@ -93,13 +93,12 @@ def install_ds(config): # 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", + if ipautil.file_exists(config.dir + "/dscert.p12"): + pkcs12_info = (config.dir + "/dscert.p12", config.dir + "/pwdfile.txt") ds = dsinstance.DsInstance() - ds.create_instance(config.ds_user, config.realm_name, config.host_name, config.dirman_password, - pkcs12_info) + ds.create_instance(config.ds_user, config.realm_name, config.host_name, config.dirman_password, pkcs12_info) def install_krb(config): krb = krbinstance.KrbInstance() @@ -108,8 +107,25 @@ def install_krb(config): config.dirman_password, ldappwd_filename) def install_http(config): + # 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 + "/httpcert.p12"): + pkcs12_info = (config.dir + "/httpcert.p12", + config.dir + "/pwdfile.txt") + http = httpinstance.HTTPInstance() - http.create_instance(config.realm_name, config.host_name) + http.create_instance(config.realm_name, config.host_name, False, pkcs12_info) + + # Now copy the autoconfiguration files + try: + shutil.copy(config.dir + "/preferences.html", "/usr/share/ipa/html/preferences.html") + shutil.copy(config.dir + "/configure.jar", "/usr/share/ipa/html/configure.jar") + shutil.copy(config.dir + "/ca.crt", "/usr/share/ipa/html/ca.crt") + except Exception, e: + print "error copying files: " + str(e) + sys.exit(1) def main(): options, filename = parse_options() @@ -137,18 +153,20 @@ def main(): install_http(config) # Create a Web Gui instance - webgui = webguiinstance.WebGuiInstance() + webgui = httpinstance.WebGuiInstance() webgui.create_instance() # Configure ntpd ntp = ntpinstance.NTPInstance() ntp.create_instance() - service.restart("dirsrv") service.restart("krb5kdc") try: + if not os.geteuid()==0: + sys.exit("\nYou must be root to run this script.\n") + main() except Exception, e: print "creation of replica failed: %s" % str(e) @@ -157,4 +175,3 @@ except Exception, e: 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 ba8455588..54f507dcc 100644 --- a/ipa-server/ipa-install/ipa-replica-prepare +++ b/ipa-server/ipa-install/ipa-replica-prepare @@ -21,12 +21,30 @@ import sys import logging, tempfile, shutil, os, pwd +import traceback from ConfigParser import SafeConfigParser import krbV +from optparse import OptionParser +import ipa.config from ipa import ipautil from ipaserver import dsinstance, installutils, certs +def usage(): + print "ipa-replica-prepate FQDN (e.g. replica.example.com)" + sys.exit(1) + +def parse_options(): + parser = OptionParser() + + args = ipa.config.init_config(sys.argv) + options, args = parser.parse_args(args) + + if len(args) != 2: + parser.error("must provide the fully-qualified name of the replica") + + return options, args + def get_host_name(): hostname = installutils.get_fqdn() try: @@ -42,22 +60,31 @@ def get_realm_name(): return c.default_realm def check_ipa_configuration(realm_name): - config_dir = dsinstance.config_dirname(realm_name) + config_dir = dsinstance.config_dirname(dsinstance.realm_to_serverid(realm_name)) if not ipautil.dir_exists(config_dir): logging.error("could not find directory instance: %s" % config_dir) sys.exit(1) -def export_certdb(ds_dir, dir): - ds_cdb = certs.CertDB(ds_dir) - - pkcs12_fname = dir + "/cacert.p12" +def export_certdb(realm_name, ds_dir, dir, fname, subject): + """realm is the kerberos realm for the IPA server. + ds_dir is the location of the master DS we are creating a replica for. + dir is the location of the files for the replica we are creating. + fname is the filename of the PKCS#12 file for this cert (minus the .p12). + subject is the subject of the certificate we are creating + """ + ds_ca = certs.CertDB(dsinstance.config_dirname(dsinstance.realm_to_serverid(realm_name))) + ca = certs.CertDB(dir) + ca.create_from_cacert(ds_ca.cacert_fname) + ca.create_server_cert("Server-Cert", subject, ds_ca) + + pkcs12_fname = dir + "/" + fname + ".p12" passwd_fname = dir + "/pwdfile.txt" fd = open(passwd_fname, "w") fd.write("\n") fd.close() try: - ds_cdb.export_pkcs12(pkcs12_fname, passwd_fname) + ca.export_pkcs12(pkcs12_fname, passwd_fname, "Server-Cert") except ipautil.CalledProcessError, e: print "error exporting CA certificate: " + str(e) try: @@ -66,6 +93,9 @@ def export_certdb(ds_dir, dir): except: pass + os.unlink(dir + "/cert8.db") + os.unlink(dir + "/key3.db") + os.unlink(dir + "/secmod.db") def get_ds_user(ds_dir): uid = os.stat(ds_dir).st_uid @@ -83,31 +113,60 @@ def save_config(dir, realm_name, host_name, ds_user): config.write(fd) def copy_files(realm_name, dir): - shutil.copy("/var/kerberos/krb5kdc/ldappwd", dir + "/ldappwd") + config_dir = dsinstance.config_dirname(dsinstance.realm_to_serverid(realm_name)) + try: + shutil.copy("/var/kerberos/krb5kdc/ldappwd", dir + "/ldappwd") + shutil.copy("/usr/share/ipa/html/preferences.html", dir + "/preferences.html") + shutil.copy("/usr/share/ipa/html/configure.jar", dir + "/configure.jar") + shutil.copy(config_dir + "/cacert.asc", dir + "/ca.crt") + except Exception, e: + print "error copying files: " + str(e) + sys.exit(1) def main(): + options, args = parse_options() + + replica_fqdn = args[1] + realm_name = get_realm_name() + check_ipa_configuration(realm_name) + host_name = get_host_name() ds_dir = dsinstance.config_dirname(realm_name) ds_user = get_ds_user(ds_dir) - check_ipa_configuration(realm_name) + print "Preparing replica for %s from %s" % (replica_fqdn, host_name) top_dir = tempfile.mkdtemp("ipa") dir = top_dir + "/realm_info" os.mkdir(dir, 0700) - export_certdb(ds_dir, dir) + print "Creating SSL certificate for the Directory Server" + export_certdb(realm_name, ds_dir, dir, "dscert", "cn=%s,ou=Fedora Directory Server" % replica_fqdn) + print "Creating SSL certificate for the Web Server" + export_certdb(realm_name, ds_dir, dir, "httpcert", "cn=%s,ou=Apache Web Server" % replica_fqdn) + print "Copying additional files" copy_files(realm_name, dir) + print "Finalizing configuration" save_config(dir, realm_name, host_name, ds_user) + print "Packaging the replica into %s" % "replica-info-" + realm_name ipautil.run(["/bin/tar", "cfz", "replica-info-" + realm_name, "-C", top_dir, "realm_info"]) shutil.rmtree(dir) -main() - +try: + if not os.geteuid()==0: + sys.exit("\nYou must be root to run this script.\n") + if not ipautil.file_exists("/usr/share/ipa/serial"): + sys.exist("The replica must be created on the primary IPA server.") - - - + main() +except Exception, e: + print "preparation 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) + print message + sys.exit(1) diff --git a/ipa-server/ipa-install/ipa-server-install b/ipa-server/ipa-install/ipa-server-install index a7405f692..4f5354fa8 100644 --- a/ipa-server/ipa-install/ipa-server-install +++ b/ipa-server/ipa-install/ipa-server-install @@ -496,7 +496,7 @@ def main(): print "\t\t * 88, 464: kerberos" print "\t\t * 123: ntp" print "" - print "\t2. You can now obtain a kerberos ticket using the command: 'kinit admin'." + print "\t2. You can now obtain a kerberos ticket using the command: 'kinit admin'" print "\t This ticket will allow you to use the IPA tools (e.g., ipa-adduser)" print "\t and the web user interface." @@ -504,7 +504,8 @@ def main(): print "\t3. Kerberos requires time synchronization between clients" print "\t and servers for correct operation. You should consider enabling ntpd." - + print "" + print "Be sure to back up the CA certificate stored in " + ipaserver.dsinstance.config_dirname(ds.serverid) + "cacert.p12" return 0 diff --git a/ipa-server/ipaserver/certs.py b/ipa-server/ipaserver/certs.py index b39cf2244..30568e6e4 100644 --- a/ipa-server/ipaserver/certs.py +++ b/ipa-server/ipaserver/certs.py @@ -19,6 +19,7 @@ import os, stat, subprocess, re import sha +import errno from ipa import ipautil @@ -42,7 +43,7 @@ class CertDB(object): # responsibility of the caller for now. In the # future we might automatically determine this # for a given db. - self.cur_serial = 1000 + self.cur_serial = -1 self.cacert_name = "CA certificate" self.valid_months = "120" @@ -57,10 +58,40 @@ class CertDB(object): self.uid = mode[stat.ST_UID] self.gid = mode[stat.ST_GID] + def set_serial_from_pkcs12(self): + """A CA cert was loaded from a PKCS#12 file. Set up our serial file""" + + self.cur_serial = self.find_cacert_serial() + try: + f=open("/usr/share/ipa/serial","w") + f.write(str(self.cur_serial)) + f.close() + except IOError, e: + raise RuntimeError("Unable to increment serial number: %s" % str(e)) + def next_serial(self): - r = self.cur_serial - self.cur_serial += 1 - return str(r) + try: + f=open("/usr/share/ipa/serial","r") + r = f.readline() + self.cur_serial = int(r) + 1 + f.close() + except IOError, e: + if e.errno == errno.ENOENT: + self.cur_serial = 1000 + f=open("/usr/share/ipa/serial","w") + f.write(str(self.cur_serial)) + f.close() + else: + raise RuntimeError("Unable to determine serial number: %s" % str(e)) + + try: + f=open("/usr/share/ipa/serial","w") + f.write(str(self.cur_serial)) + f.close() + except IOError, e: + raise RuntimeError("Unable to increment serial number: %s" % str(e)) + + return str(self.cur_serial) def set_perms(self, fname, write=False): os.chown(fname, self.uid, self.gid) @@ -75,7 +106,7 @@ class CertDB(object): def run_certutil(self, args, stdin=None): new_args = ["/usr/bin/certutil", "-d", self.secdir] new_args = new_args + args - ipautil.run(new_args, stdin) + return ipautil.run(new_args, stdin) def run_signtool(self, args, stdin=None): new_args = ["/usr/bin/signtool", "-d", self.secdir] @@ -139,6 +170,16 @@ class CertDB(object): "-t", "CT,,C", "-a", "-i", cacert_fname]) + + def find_cacert_serial(self): + (out,err) = self.run_certutil(["-L", "-n", self.cacert_name]) + data = out.split('\n') + for line in data: + x = re.match(r'\s+Serial Number: (\d+) .*', line) + if x is not None: + return x.group(1) + + raise RuntimeError("Unable to find serial number") def create_server_cert(self, nickname, name, other_certdb=None): cdb = other_certdb @@ -330,5 +371,3 @@ class CertDB(object): sysrestore.backup_file(self.pin_fname) sysrestore.backup_file(self.certreq_fname) sysrestore.backup_file(self.certder_fname) - - diff --git a/ipa-server/ipaserver/dsinstance.py b/ipa-server/ipaserver/dsinstance.py index 733e5d5be..a320a1be6 100644 --- a/ipa-server/ipaserver/dsinstance.py +++ b/ipa-server/ipaserver/dsinstance.py @@ -257,10 +257,9 @@ class DsInstance(service.Service): ca = certs.CertDB(dirname) if self.pkcs12_info: ca.create_from_pkcs12(self.pkcs12_info[0], self.pkcs12_info[1]) - ca.cur_serial = 2100 else: ca.create_self_signed() - ca.create_server_cert("Server-Cert", "cn=%s,ou=Fedora Directory Server" % self.host_name) + ca.create_server_cert("Server-Cert", "cn=%s,ou=Fedora Directory Server" % self.host_name) conn = ipaldap.IPAdmin("127.0.0.1") conn.simple_bind_s("cn=directory manager", self.dm_password) diff --git a/ipa-server/ipaserver/httpinstance.py b/ipa-server/ipaserver/httpinstance.py index 12e5ae105..bee77e9fb 100644 --- a/ipa-server/ipaserver/httpinstance.py +++ b/ipa-server/ipaserver/httpinstance.py @@ -55,10 +55,11 @@ class HTTPInstance(service.Service): def __init__(self): service.Service.__init__(self, "httpd") - def create_instance(self, realm, fqdn): + def create_instance(self, realm, fqdn, autoconfig=True, pkcs12_info=None): self.fqdn = fqdn self.realm = realm self.domain = fqdn[fqdn.find(".")+1:] + self.pkcs12_info = pkcs12_info self.sub_dict = { "REALM" : realm, "FQDN": fqdn, "DOMAIN" : self.domain } self.step("disabling mod_ssl in httpd", self.__disable_mod_ssl) @@ -66,7 +67,8 @@ class HTTPInstance(service.Service): self.step("configuring httpd", self.__configure_http) self.step("creating a keytab for httpd", self.__create_http_keytab) self.step("Setting up ssl", self.__setup_ssl) - self.step("Setting up browser autoconfig", self.__setup_autoconfig) + if autoconfig: + self.step("Setting up browser autoconfig", self.__setup_autoconfig) self.step("configuring SELinux for httpd", self.__selinux_config) self.step("restarting httpd", self.__start) self.step("configuring httpd to start on boot", self.__enable) @@ -136,10 +138,12 @@ class HTTPInstance(service.Service): def __setup_ssl(self): ds_ca = certs.CertDB(dsinstance.config_dirname(dsinstance.realm_to_serverid(self.realm))) ca = certs.CertDB(NSS_DIR) - ds_ca.cur_serial = 2000 - ca.create_from_cacert(ds_ca.cacert_fname) - ca.create_server_cert("Server-Cert", "cn=%s,ou=Apache Web Server" % self.fqdn, ds_ca) - ca.create_signing_cert("Signing-Cert", "cn=%s,ou=Signing Certificate,o=Identity Policy Audit" % self.fqdn, ds_ca) + if self.pkcs12_info: + ca.create_from_pkcs12(self.pkcs12_info[0], self.pkcs12_info[1], passwd=False) + else: + ca.create_from_cacert(ds_ca.cacert_fname) + ca.create_server_cert("Server-Cert", "cn=%s,ou=Apache Web Server" % self.fqdn, ds_ca) + ca.create_signing_cert("Signing-Cert", "cn=%s,ou=Signing Certificate,o=Identity Policy Audit" % self.fqdn, ds_ca) def __setup_autoconfig(self): prefs_txt = ipautil.template_file(ipautil.SHARE_DIR + "preferences.html.template", self.sub_dict) diff --git a/ipa-server/ipaserver/replication.py b/ipa-server/ipaserver/replication.py index 765905e55..10addc65b 100644 --- a/ipa-server/ipaserver/replication.py +++ b/ipa-server/ipaserver/replication.py @@ -279,7 +279,7 @@ class ReplicationManager: return haserror def start_replication(self, other_conn): - print "starting replication" + print "Starting replication, please wait until this has completed." cn, dn = self.agreement_dn(self.conn) mod = [(ldap.MOD_ADD, 'nsds5BeginReplicaRefresh', 'start')]