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 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)

View File

@ -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)
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 + "/cacert.p12"
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)

View File

@ -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

View File

@ -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]
@ -140,6 +171,16 @@ class CertDB(object):
"-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
if not cdb:
@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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')]