Use file to store the current CA serial number

No longer create a PKCS#12 file that contains the CA
No longer send the entire CA to each replica, generate the SSL certs on master
Fix number of bugs in ipa-replica-install and prepare
Produce status output during replica creation
This commit is contained in:
Rob Crittenden 2008-02-05 12:23:53 -05:00
parent 25057816a5
commit 5a96618f5d
7 changed files with 161 additions and 42 deletions

View File

@ -20,13 +20,13 @@
import sys import sys
import tempfile, os, pwd, traceback, logging import tempfile, os, pwd, traceback, logging, shutil
from ConfigParser import SafeConfigParser from ConfigParser import SafeConfigParser
from ipa import ipautil from ipa import ipautil
from ipaserver import dsinstance, replication, installutils, krbinstance, service from ipaserver import dsinstance, replication, installutils, krbinstance, service
from ipaserver import httpinstance, webguiinstance, ntpinstance, certs from ipaserver import httpinstance, ntpinstance, certs
class ReplicaConfig: class ReplicaConfig:
def __init__(self): def __init__(self):
@ -93,13 +93,12 @@ def install_ds(config):
# that. Otherwise the ds setup will create the CA # that. Otherwise the ds setup will create the CA
# cert # cert
pkcs12_info = None pkcs12_info = None
if ipautil.file_exists(config.dir + "/cacert.p12"): if ipautil.file_exists(config.dir + "/dscert.p12"):
pkcs12_info = (config.dir + "/cacert.p12", pkcs12_info = (config.dir + "/dscert.p12",
config.dir + "/pwdfile.txt") config.dir + "/pwdfile.txt")
ds = dsinstance.DsInstance() 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)
pkcs12_info)
def install_krb(config): def install_krb(config):
krb = krbinstance.KrbInstance() krb = krbinstance.KrbInstance()
@ -108,8 +107,25 @@ def install_krb(config):
config.dirman_password, ldappwd_filename) config.dirman_password, ldappwd_filename)
def install_http(config): 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 = 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(): def main():
options, filename = parse_options() options, filename = parse_options()
@ -137,18 +153,20 @@ def main():
install_http(config) install_http(config)
# Create a Web Gui instance # Create a Web Gui instance
webgui = webguiinstance.WebGuiInstance() webgui = httpinstance.WebGuiInstance()
webgui.create_instance() webgui.create_instance()
# Configure ntpd # Configure ntpd
ntp = ntpinstance.NTPInstance() ntp = ntpinstance.NTPInstance()
ntp.create_instance() ntp.create_instance()
service.restart("dirsrv") service.restart("dirsrv")
service.restart("krb5kdc") service.restart("krb5kdc")
try: try:
if not os.geteuid()==0:
sys.exit("\nYou must be root to run this script.\n")
main() main()
except Exception, e: except Exception, e:
print "creation of replica failed: %s" % str(e) print "creation of replica failed: %s" % str(e)
@ -157,4 +175,3 @@ except Exception, e:
message = message + "\n" + str message = message + "\n" + str
logging.debug(message) logging.debug(message)
sys.exit(1) sys.exit(1)

View File

@ -21,12 +21,30 @@
import sys import sys
import logging, tempfile, shutil, os, pwd import logging, tempfile, shutil, os, pwd
import traceback
from ConfigParser import SafeConfigParser from ConfigParser import SafeConfigParser
import krbV import krbV
from optparse import OptionParser
import ipa.config
from ipa import ipautil from ipa import ipautil
from ipaserver import dsinstance, installutils, certs 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(): def get_host_name():
hostname = installutils.get_fqdn() hostname = installutils.get_fqdn()
try: try:
@ -42,22 +60,31 @@ def get_realm_name():
return c.default_realm return c.default_realm
def check_ipa_configuration(realm_name): 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): if not ipautil.dir_exists(config_dir):
logging.error("could not find directory instance: %s" % config_dir) logging.error("could not find directory instance: %s" % config_dir)
sys.exit(1) sys.exit(1)
def export_certdb(ds_dir, dir): def export_certdb(realm_name, ds_dir, dir, fname, subject):
ds_cdb = certs.CertDB(ds_dir) """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 + "/cacert.p12" pkcs12_fname = dir + "/" + fname + ".p12"
passwd_fname = dir + "/pwdfile.txt" passwd_fname = dir + "/pwdfile.txt"
fd = open(passwd_fname, "w") fd = open(passwd_fname, "w")
fd.write("\n") fd.write("\n")
fd.close() fd.close()
try: try:
ds_cdb.export_pkcs12(pkcs12_fname, passwd_fname) ca.export_pkcs12(pkcs12_fname, passwd_fname, "Server-Cert")
except ipautil.CalledProcessError, e: except ipautil.CalledProcessError, e:
print "error exporting CA certificate: " + str(e) print "error exporting CA certificate: " + str(e)
try: try:
@ -66,6 +93,9 @@ def export_certdb(ds_dir, dir):
except: except:
pass pass
os.unlink(dir + "/cert8.db")
os.unlink(dir + "/key3.db")
os.unlink(dir + "/secmod.db")
def get_ds_user(ds_dir): def get_ds_user(ds_dir):
uid = os.stat(ds_dir).st_uid uid = os.stat(ds_dir).st_uid
@ -83,31 +113,60 @@ def save_config(dir, realm_name, host_name, ds_user):
config.write(fd) config.write(fd)
def copy_files(realm_name, dir): def copy_files(realm_name, dir):
config_dir = dsinstance.config_dirname(dsinstance.realm_to_serverid(realm_name))
try:
shutil.copy("/var/kerberos/krb5kdc/ldappwd", dir + "/ldappwd") 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(): def main():
options, args = parse_options()
replica_fqdn = args[1]
realm_name = get_realm_name() realm_name = get_realm_name()
check_ipa_configuration(realm_name)
host_name = get_host_name() host_name = get_host_name()
ds_dir = dsinstance.config_dirname(realm_name) ds_dir = dsinstance.config_dirname(realm_name)
ds_user = get_ds_user(ds_dir) 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") top_dir = tempfile.mkdtemp("ipa")
dir = top_dir + "/realm_info" dir = top_dir + "/realm_info"
os.mkdir(dir, 0700) 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) copy_files(realm_name, dir)
print "Finalizing configuration"
save_config(dir, realm_name, host_name, ds_user) 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"]) ipautil.run(["/bin/tar", "cfz", "replica-info-" + realm_name, "-C", top_dir, "realm_info"])
shutil.rmtree(dir) 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)

View File

@ -496,7 +496,7 @@ def main():
print "\t\t * 88, 464: kerberos" print "\t\t * 88, 464: kerberos"
print "\t\t * 123: ntp" print "\t\t * 123: ntp"
print "" 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 This ticket will allow you to use the IPA tools (e.g., ipa-adduser)"
print "\t and the web user interface." print "\t and the web user interface."
@ -504,7 +504,8 @@ def main():
print "\t3. Kerberos requires time synchronization between clients" print "\t3. Kerberos requires time synchronization between clients"
print "\t and servers for correct operation. You should consider enabling ntpd." 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 return 0

View File

@ -19,6 +19,7 @@
import os, stat, subprocess, re import os, stat, subprocess, re
import sha import sha
import errno
from ipa import ipautil from ipa import ipautil
@ -42,7 +43,7 @@ class CertDB(object):
# responsibility of the caller for now. In the # responsibility of the caller for now. In the
# future we might automatically determine this # future we might automatically determine this
# for a given db. # for a given db.
self.cur_serial = 1000 self.cur_serial = -1
self.cacert_name = "CA certificate" self.cacert_name = "CA certificate"
self.valid_months = "120" self.valid_months = "120"
@ -57,10 +58,40 @@ class CertDB(object):
self.uid = mode[stat.ST_UID] self.uid = mode[stat.ST_UID]
self.gid = mode[stat.ST_GID] 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): def next_serial(self):
r = self.cur_serial try:
self.cur_serial += 1 f=open("/usr/share/ipa/serial","r")
return str(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): def set_perms(self, fname, write=False):
os.chown(fname, self.uid, self.gid) os.chown(fname, self.uid, self.gid)
@ -75,7 +106,7 @@ class CertDB(object):
def run_certutil(self, args, stdin=None): def run_certutil(self, args, stdin=None):
new_args = ["/usr/bin/certutil", "-d", self.secdir] new_args = ["/usr/bin/certutil", "-d", self.secdir]
new_args = new_args + args new_args = new_args + args
ipautil.run(new_args, stdin) return ipautil.run(new_args, stdin)
def run_signtool(self, args, stdin=None): def run_signtool(self, args, stdin=None):
new_args = ["/usr/bin/signtool", "-d", self.secdir] new_args = ["/usr/bin/signtool", "-d", self.secdir]
@ -140,6 +171,16 @@ class CertDB(object):
"-a", "-a",
"-i", cacert_fname]) "-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): def create_server_cert(self, nickname, name, other_certdb=None):
cdb = other_certdb cdb = other_certdb
if not cdb: if not cdb:
@ -330,5 +371,3 @@ class CertDB(object):
sysrestore.backup_file(self.pin_fname) sysrestore.backup_file(self.pin_fname)
sysrestore.backup_file(self.certreq_fname) sysrestore.backup_file(self.certreq_fname)
sysrestore.backup_file(self.certder_fname) sysrestore.backup_file(self.certder_fname)

View File

@ -257,7 +257,6 @@ class DsInstance(service.Service):
ca = certs.CertDB(dirname) ca = certs.CertDB(dirname)
if self.pkcs12_info: if self.pkcs12_info:
ca.create_from_pkcs12(self.pkcs12_info[0], self.pkcs12_info[1]) ca.create_from_pkcs12(self.pkcs12_info[0], self.pkcs12_info[1])
ca.cur_serial = 2100
else: else:
ca.create_self_signed() 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)

View File

@ -55,10 +55,11 @@ class HTTPInstance(service.Service):
def __init__(self): def __init__(self):
service.Service.__init__(self, "httpd") 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.fqdn = fqdn
self.realm = realm self.realm = realm
self.domain = fqdn[fqdn.find(".")+1:] self.domain = fqdn[fqdn.find(".")+1:]
self.pkcs12_info = pkcs12_info
self.sub_dict = { "REALM" : realm, "FQDN": fqdn, "DOMAIN" : self.domain } self.sub_dict = { "REALM" : realm, "FQDN": fqdn, "DOMAIN" : self.domain }
self.step("disabling mod_ssl in httpd", self.__disable_mod_ssl) self.step("disabling mod_ssl in httpd", self.__disable_mod_ssl)
@ -66,6 +67,7 @@ class HTTPInstance(service.Service):
self.step("configuring httpd", self.__configure_http) self.step("configuring httpd", self.__configure_http)
self.step("creating a keytab for httpd", self.__create_http_keytab) self.step("creating a keytab for httpd", self.__create_http_keytab)
self.step("Setting up ssl", self.__setup_ssl) self.step("Setting up ssl", self.__setup_ssl)
if autoconfig:
self.step("Setting up browser autoconfig", self.__setup_autoconfig) self.step("Setting up browser autoconfig", self.__setup_autoconfig)
self.step("configuring SELinux for httpd", self.__selinux_config) self.step("configuring SELinux for httpd", self.__selinux_config)
self.step("restarting httpd", self.__start) self.step("restarting httpd", self.__start)
@ -136,7 +138,9 @@ class HTTPInstance(service.Service):
def __setup_ssl(self): def __setup_ssl(self):
ds_ca = certs.CertDB(dsinstance.config_dirname(dsinstance.realm_to_serverid(self.realm))) ds_ca = certs.CertDB(dsinstance.config_dirname(dsinstance.realm_to_serverid(self.realm)))
ca = certs.CertDB(NSS_DIR) ca = certs.CertDB(NSS_DIR)
ds_ca.cur_serial = 2000 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_from_cacert(ds_ca.cacert_fname)
ca.create_server_cert("Server-Cert", "cn=%s,ou=Apache Web Server" % self.fqdn, ds_ca) 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) ca.create_signing_cert("Signing-Cert", "cn=%s,ou=Signing Certificate,o=Identity Policy Audit" % self.fqdn, ds_ca)

View File

@ -279,7 +279,7 @@ class ReplicationManager:
return haserror return haserror
def start_replication(self, other_conn): 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) cn, dn = self.agreement_dn(self.conn)
mod = [(ldap.MOD_ADD, 'nsds5BeginReplicaRefresh', 'start')] mod = [(ldap.MOD_ADD, 'nsds5BeginReplicaRefresh', 'start')]