Allow replicas of an IPA server using an internal dogtag server as the CA

This involves creating a new CA instance on the replica and using pkisilent
to create a clone of the master CA.

Also generally fixes IPA to work with the latest dogtag SVN tip. A lot of
changes to ports and configuration have been done recently.
This commit is contained in:
Rob Crittenden
2009-07-10 16:18:16 -04:00
parent 904e555404
commit 8d164569d0
9 changed files with 299 additions and 116 deletions

View File

@@ -101,6 +101,25 @@ def set_owner(config, dir):
pw = pwd.getpwnam(config.ds_user) pw = pwd.getpwnam(config.ds_user)
os.chown(dir, pw.pw_uid, pw.pw_gid) os.chown(dir, pw.pw_uid, pw.pw_gid)
def install_ca(config):
cafile = config.dir + "/ca.p12"
if not ipautil.file_exists(cafile):
return None
try:
from ipaserver.install import cainstance
except ImportError:
print >> sys.stderr, "Import failed: %s" % sys.exc_value
sys.exit(1)
cs = cainstance.CADSInstance()
cs.create_instance(config.ds_user, config.realm_name, config.host_name, config.domain_name, config.dirman_password)
ca = cainstance.CAInstance()
ca.configure_instance("pkiuser", config.host_name, config.dirman_password, config.dirman_password, pkcs12_info=(cafile,), master_host=config.master_host_name)
return ca
def install_ds(config): def install_ds(config):
dsinstance.check_existing_installation() dsinstance.check_existing_installation()
dsinstance.check_ports() dsinstance.check_ports()
@@ -237,17 +256,20 @@ def main():
except ldap.INVALID_CREDENTIALS, e : except ldap.INVALID_CREDENTIALS, e :
sys.exit("\nThe password provided is incorrect for LDAP server %s" % config.master_host_name) sys.exit("\nThe password provided is incorrect for LDAP server %s" % config.master_host_name)
# Install CA cert so that we can do SSL connections with ldap
install_ca_cert(config)
# Configure ntpd # Configure ntpd
if options.conf_ntp: if options.conf_ntp:
ntp = ntpinstance.NTPInstance() ntp = ntpinstance.NTPInstance()
ntp.create_instance() ntp.create_instance()
# Configure the CA if necessary
CA = install_ca(config)
# Configure dirsrv # Configure dirsrv
ds = install_ds(config) ds = install_ds(config)
# Install CA cert so that we can do SSL connections with ldap
install_ca_cert(config)
try: try:
repl = replication.ReplicationManager(config.host_name, config.dirman_password) repl = replication.ReplicationManager(config.host_name, config.dirman_password)
ret = repl.setup_replication(config.master_host_name, config.realm_name) ret = repl.setup_replication(config.master_host_name, config.realm_name)
@@ -259,6 +281,10 @@ def main():
install_krb(config) install_krb(config)
install_http(config) install_http(config)
if CA:
CA.import_ra_cert(dir + "/ra.p12")
CA.fix_ra_perms()
service.restart("httpd")
# Create the config file # Create the config file
fd = open("/etc/ipa/ipa.conf", "w") fd = open("/etc/ipa/ipa.conf", "w")
@@ -275,8 +301,7 @@ def main():
fd.write("realm=" + config.realm_name + "\n") fd.write("realm=" + config.realm_name + "\n")
fd.write("domain=" + config.domain_name + "\n") fd.write("domain=" + config.domain_name + "\n")
fd.write("xmlrpc_uri=https://%s/ipa/xml\n" % config.host_name) fd.write("xmlrpc_uri=https://%s/ipa/xml\n" % config.host_name)
# FIXME: detect when we are installing a cloned CA if ipautil.file_exists(config.dir + "/ca.p12"):
if False:
fd.write("enable_ra=True\n") fd.write("enable_ra=True\n")
fd.close() fd.close()

View File

@@ -28,7 +28,7 @@ from optparse import OptionParser
import ipapython.config import ipapython.config
from ipapython import ipautil from ipapython import ipautil
from ipaserver.install import dsinstance, installutils, certs from ipaserver.install import dsinstance, installutils, certs, httpinstance
from ipaserver import ipaldap from ipaserver import ipaldap
from ipapython import version from ipapython import version
import ldap import ldap
@@ -98,28 +98,37 @@ def check_ipa_configuration(realm_name):
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(realm_name, ds_dir, dir, passwd_fname, fname, subject): def export_certdb(realm_name, ds_dir, dir, passwd_fname, fname, hostname):
"""realm is the kerberos realm for the IPA server. """realm is the kerberos realm for the IPA server.
ds_dir is the location of the master DS we are creating a replica for. 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. dir is the location of the files for the replica we are creating.
passwd_fname is the file containing the PKCS#12 password passwd_fname is the file containing the PKCS#12 password
fname is the filename of the PKCS#12 file for this cert (minus the .p12). 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 hostname is the FQDN of the server we're creating a cert for.
The subject is handled by certs.CertDB:create_server_cert()
""" """
try: try:
ds_ca = certs.CertDB(dsinstance.config_dirname(dsinstance.realm_to_serverid(realm_name))) self_signed = certs.ipa_self_signed()
ca = certs.CertDB(dir)
ca.create_from_cacert(ds_ca.cacert_fname) db = certs.CertDB(dir)
ca.create_server_cert("Server-Cert", subject, ds_ca) db.create_passwd_file()
db.create_certdbs()
if self_signed:
ca_db = certs.CertDB(dsinstance.config_dirname(dsinstance.realm_to_serverid(realm_name)))
db.create_from_cacert(ca_db.cacert_fname)
else:
ca_db = certs.CertDB(httpinstance.NSS_DIR, host_name=get_host_name())
db.create_server_cert("Server-Cert", hostname, ca_db)
except Exception, e: except Exception, e:
raise e raise e
pkcs12_fname = dir + "/" + fname + ".p12" pkcs12_fname = dir + "/" + fname + ".p12"
try: try:
ca.export_pkcs12(pkcs12_fname, passwd_fname, "Server-Cert") db.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 Server certificate: " + str(e)
remove_file(pkcs12_fname) remove_file(pkcs12_fname)
remove_file(passwd_fname) remove_file(passwd_fname)
@@ -130,6 +139,32 @@ def export_certdb(realm_name, ds_dir, dir, passwd_fname, fname, subject):
if ipautil.file_exists(passwd_fname + ".orig"): if ipautil.file_exists(passwd_fname + ".orig"):
remove_file(passwd_fname + ".orig") remove_file(passwd_fname + ".orig")
def export_ra_pkcs12(dir, dm_password):
"""
dir is the location of the files for the replica we are creating.
dm_password is the Directory Manager password
If this install is using dogtag/RHCS then export the RA certificate.
"""
if certs.ipa_self_signed():
return
(agent_fd, agent_name) = tempfile.mkstemp()
os.write(agent_fd, dm_password)
os.close(agent_fd)
try:
try:
db = certs.CertDB(httpinstance.NSS_DIR, host_name=get_host_name())
if db.has_nickname("ipaCert"):
pkcs12_fname = "%s/ra.p12" % dir
db.export_pkcs12(pkcs12_fname, agent_name, "ipaCert")
except Exception, e:
raise e
finally:
os.remove(agent_name)
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
user = pwd.getpwuid(uid)[0] user = pwd.getpwuid(uid)[0]
@@ -176,7 +211,8 @@ def main():
replica_fqdn = args[0] replica_fqdn = args[0]
if not ipautil.file_exists(certs.CA_SERIALNO) and not options.dirsrv_pin: # FIXME: need more robust way to determine if dogtag is configured
if not certs.ipa_self_signed() and not ipautil.file_exists("/var/lib/pki-ca") and not options.dirsrv_pin:
sys.exit("The replica must be created on the primary IPA server.\nIf you installed IPA with your own certificates using PKCS#12 files you must provide PKCS#12 files for any replicas you create as well.") sys.exit("The replica must be created on the primary IPA server.\nIf you installed IPA with your own certificates using PKCS#12 files you must provide PKCS#12 files for any replicas you create as well.")
print "Determining current realm name" print "Determining current realm name"
@@ -244,8 +280,18 @@ def main():
print "Copy failed %s" % e print "Copy failed %s" % e
sys.exit(1) sys.exit(1)
else: else:
try:
if not certs.ipa_self_signed():
# FIXME, need option for location of CA backup
if ipautil.file_exists("/root/tmp-ca.p12"):
shutil.copy("/root/tmp-ca.p12", dir + "/ca.p12")
else:
raise RuntimeError("Root CA PKCS#12 not found in /root/tmp-ca.p12")
except IOError, e:
print "Copy failed %s" % e
sys.exit(1)
print "Creating SSL certificate for the Directory Server" print "Creating SSL certificate for the Directory Server"
export_certdb(realm_name, ds_dir, dir, passwd_fname, "dscert", "cn=%s,ou=Fedora Directory Server" % replica_fqdn) export_certdb(realm_name, ds_dir, dir, passwd_fname, "dscert", replica_fqdn)
if options.http_pin: if options.http_pin:
passwd = options.http_pin passwd = options.http_pin
@@ -266,7 +312,9 @@ def main():
sys.exit(1) sys.exit(1)
else: else:
print "Creating SSL certificate for the Web Server" print "Creating SSL certificate for the Web Server"
export_certdb(realm_name, ds_dir, dir, passwd_fname, "httpcert", "cn=%s,ou=Apache Web Server" % replica_fqdn) export_certdb(realm_name, ds_dir, dir, passwd_fname, "httpcert", replica_fqdn)
print "Exporting RA certificate"
export_ra_pkcs12(dir, dirman_password)
print "Copying additional files" print "Copying additional files"
copy_files(realm_name, dir) copy_files(realm_name, dir)
print "Finalizing configuration" print "Finalizing configuration"

View File

@@ -521,7 +521,7 @@ def main():
pass pass
cs = cainstance.CADSInstance() cs = cainstance.CADSInstance()
cs.create_instance("dirsrv", realm_name, host_name, domain_name, dm_password) cs.create_instance(ds_user, realm_name, host_name, domain_name, dm_password)
ca = cainstance.CAInstance() ca = cainstance.CAInstance()
ca.configure_instance("pkiuser", host_name, dm_password, dm_password) ca.configure_instance("pkiuser", host_name, dm_password, dm_password)

View File

@@ -114,7 +114,7 @@ DEFAULT_CONFIG = (
# CA plugin: # CA plugin:
('ca_host', object), # Set in Env._finalize_core() ('ca_host', object), # Set in Env._finalize_core()
('ca_port', 9180), ('ca_port', 9180),
('ca_ssl_port', 9444), ('ca_ssl_port', 9443),
# Special CLI: # Special CLI:
('prompt_all', False), ('prompt_all', False),

View File

@@ -21,12 +21,14 @@ from ipalib import api, errors
import httplib import httplib
import xml.dom.minidom import xml.dom.minidom
def get_ca_certchain(): def get_ca_certchain(ca_host=None):
""" """
Retrieve the CA Certificate chain from the configured Dogtag server. Retrieve the CA Certificate chain from the configured Dogtag server.
""" """
if ca_host is None:
ca_host = api.env.ca_host
chain = None chain = None
conn = httplib.HTTPConnection(api.env.ca_host, 9180) conn = httplib.HTTPConnection(ca_host, 9180)
conn.request("GET", "/ca/ee/ca/getCertChain") conn.request("GET", "/ca/ee/ca/getCertChain")
res = conn.getresponse() res = conn.getresponse()
if res.status == 200: if res.status == 200:
@@ -42,8 +44,8 @@ def get_ca_certchain():
item_node = doc.getElementsByTagName("Error") item_node = doc.getElementsByTagName("Error")
reason = item_node[0].childNodes[0].data reason = item_node[0].childNodes[0].data
raise errors.RemoteRetrieveError(reason=reason) raise errors.RemoteRetrieveError(reason=reason)
except: except Exception, e:
raise errors.RemoteRetrieveError(reason="Retrieving CA cert chain failed") raise errors.RemoteRetrieveError(reason="Retrieving CA cert chain failed: %s" % str(e))
finally: finally:
doc.unlink() doc.unlink()

View File

@@ -51,6 +51,14 @@ from ipalib import util
DEFAULT_DSPORT=7389 DEFAULT_DSPORT=7389
# These values come from /usr/share/pki/ca/setup/postinstall
PKI_INSTANCE_NAME="pki-ca"
AGENT_SECURE_PORT=9443
EE_SECURE_PORT=9444
ADMIN_SECURE_PORT=9445
UNSECURE_PORT=9180
TOMCAT_SERVER_PORT=9701
# We need to reset the template because the CA uses the regular boot # We need to reset the template because the CA uses the regular boot
# information # information
INF_TEMPLATE = """ INF_TEMPLATE = """
@@ -88,13 +96,12 @@ def get_preop_pin(instance_root, instance_name):
return preop_pin return preop_pin
def export_pkcs12(output_file, output_passwd, nickname, cert_database, def import_pkcs12(input_file, input_passwd, cert_database,
cert_passwd): cert_passwd):
ipautil.run(["/usr/bin/pk12util", "-d", cert_database, ipautil.run(["/usr/bin/pk12util", "-d", cert_database,
"-o", output_file, "-i", input_file,
"-n", nickname,
"-k", cert_passwd, "-k", cert_passwd,
"-w", output_passwd]) "-w", input_passwd])
def client_auth_data_callback(ca_names, chosen_nickname, password, certdb): def client_auth_data_callback(ca_names, chosen_nickname, password, certdb):
cert = None cert = None
@@ -147,7 +154,7 @@ def get_defList(data):
""" """
Return a dictionary of defList name/value pairs. Return a dictionary of defList name/value pairs.
A certificate signing request is specfied as a series of these. A certificate signing request is specified as a series of these.
""" """
varname = None varname = None
value = None value = None
@@ -215,6 +222,7 @@ class CADSInstance(service.Service):
self.pkcs12_info = None self.pkcs12_info = None
self.ds_user = None self.ds_user = None
self.ds_port = None self.ds_port = None
self.master_host = None
if realm_name: if realm_name:
self.suffix = util.realm_to_suffix(self.realm_name) self.suffix = util.realm_to_suffix(self.realm_name)
self.__setup_sub_dict() self.__setup_sub_dict()
@@ -351,10 +359,13 @@ class CAInstance(service.Service):
def __init__(self): def __init__(self):
service.Service.__init__(self, "pki-ca") service.Service.__init__(self, "pki-ca")
self.pki_user = None self.pki_user = "pkiuser"
self.dm_password = None self.dm_password = None
self.admin_password = None self.admin_password = None
self.host_name = None self.host_name = None
self.pkcs12_info = None
self.clone = False
self.external = False
# The same database is used for mod_nss because the NSS context # The same database is used for mod_nss because the NSS context
# will already have been initialized by Apache by the time # will already have been initialized by Apache by the time
@@ -367,31 +378,36 @@ class CAInstance(service.Service):
self.ds_port = DEFAULT_DSPORT self.ds_port = DEFAULT_DSPORT
self.domain_name = "IPA" self.domain_name = "IPA"
self.server_root = "/var/lib" self.server_root = "/var/lib"
self.secure_port = 9444
self.ra_cert = None self.ra_cert = None
self.requestId = None self.requestId = None
def __del__(self): def __del__(self):
shutil.rmtree(self.ca_agent_db, ignore_errors=True) shutil.rmtree(self.ca_agent_db, ignore_errors=True)
def configure_instance(self, pki_user, host_name, dm_password, admin_password, ds_port=DEFAULT_DSPORT): def configure_instance(self, pki_user, host_name, dm_password, admin_password, ds_port=DEFAULT_DSPORT, pkcs12_info=None, master_host=None):
self.pki_user = pki_user self.pki_user = pki_user
self.host_name = host_name self.host_name = host_name
self.dm_password = dm_password self.dm_password = dm_password
self.admin_password = admin_password self.admin_password = admin_password
self.ds_port = ds_port self.ds_port = ds_port
self.pkcs12_info = pkcs12_info
if self.pkcs12_info is not None:
self.clone = True
self.master_host = master_host
if not ipautil.dir_exists("/var/lib/pki-ca"): if not ipautil.dir_exists("/var/lib/pki-ca"):
self.step("creating pki-ca instance", self.create_instance) self.step("creating pki-ca instance", self.create_instance)
self.step("creating certificate server user", self.__create_ca_user) self.step("creating certificate server user", self.__create_ca_user)
self.step("configuring certificate server instance", self.__configure_instance) self.step("configuring certificate server instance", self.__configure_instance)
self.step("creating CA agent PKCS#12 file in /root", self.__create_ca_agent_pkcs12) 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) 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) self.step("importing CA chain to RA certificate database", self.__import_ca_chain)
self.step("requesting RA certificate from CA", self.__request_ra_certificate) if not self.clone:
self.step("issuing RA agent certificate", self.__issue_ra_cert) self.step("requesting RA certificate from CA", self.__request_ra_certificate)
self.step("adding RA agent as a trusted user", self.__configure_ra) self.step("issuing RA agent certificate", self.__issue_ra_cert)
self.step("fixing RA database permissions", self.__fix_ra_perms) self.step("adding RA agent as a trusted user", self.__configure_ra)
self.step("fixing RA database permissions", self.fix_ra_perms)
self.step("setting up signing cert profile", self.__setup_sign_profile) self.step("setting up signing cert profile", self.__setup_sign_profile)
self.step("configuring certificate server to start on boot", self.__enable) self.step("configuring certificate server to start on boot", self.__enable)
self.step("restarting certificate server", self.__restart_instance) self.step("restarting certificate server", self.__restart_instance)
@@ -401,30 +417,35 @@ class CAInstance(service.Service):
def create_instance(self): def create_instance(self):
""" """
If for some reason the instance doesn't exist, create a new one." If for some reason the instance doesn't exist, create a new one."
These values come from /usr/share/pki/ca/setup/postinstall
""" """
PKI_INSTANCE_NAME="pki-ca"
AGENT_SECURE_PORT="9443"
EE_SECURE_PORT="9444"
ADMIN_SECURE_PORT="9445"
UNSECURE_PORT="9180"
TOMCAT_SERVER_PORT="9701"
args = ['/usr/bin/pkicreate', args = ['/usr/bin/pkicreate',
'-pki_instance_root', '/var/lib', '-pki_instance_root', '/var/lib',
'-pki_instance_name', PKI_INSTANCE_NAME, '-pki_instance_name', PKI_INSTANCE_NAME,
'-subsystem_type', 'ca', '-subsystem_type', 'ca',
'-agent_secure_port', AGENT_SECURE_PORT, '-agent_secure_port', str(AGENT_SECURE_PORT),
'-ee_secure_port', EE_SECURE_PORT, '-ee_secure_port', str(EE_SECURE_PORT),
'-admin_secure_port', ADMIN_SECURE_PORT, '-admin_secure_port', str(ADMIN_SECURE_PORT),
'-unsecure_port', UNSECURE_PORT, '-unsecure_port', str(UNSECURE_PORT),
'-tomcat_server_port', TOMCAT_SERVER_PORT, '-tomcat_server_port', str(TOMCAT_SERVER_PORT),
'-redirect', 'conf=/etc/pki-ca', '-redirect', 'conf=/etc/pki-ca',
'-redirect', 'logs=/var/log/pki-ca', '-redirect', 'logs=/var/log/pki-ca',
] ]
ipautil.run(args) ipautil.run(args)
# Turn off Nonces
if installutils.update_file('/var/lib/pki-ca/conf/CS.cfg', 'ca.enableNonces=true', 'ca.enableNonces=false') != 0:
raise RuntimeError("Disabling nonces failed")
pent = pwd.getpwnam(self.pki_user)
os.chown('/var/lib/pki-ca/conf/CS.cfg', pent.pw_uid, pent.pw_gid )
logging.debug("restarting ca instance")
try:
self.restart()
logging.debug("done restarting ca instance")
except ipautil.CalledProcessError, e:
print "failed to restart ca instance", e
def __enable(self): def __enable(self):
self.backup_state("enabled", self.is_enabled()) self.backup_state("enabled", self.is_enabled())
self.chkconfig_on() self.chkconfig_on()
@@ -455,7 +476,7 @@ class CAInstance(service.Service):
try: try:
args = ["/usr/bin/perl", "/usr/bin/pkisilent", "ConfigureCA", args = ["/usr/bin/perl", "/usr/bin/pkisilent", "ConfigureCA",
"-cs_hostname", self.host_name, "-cs_hostname", self.host_name,
"-cs_port", str(self.secure_port), "-cs_port", str(ADMIN_SECURE_PORT),
"-client_certdb_dir", self.ca_agent_db, "-client_certdb_dir", self.ca_agent_db,
"-client_certdb_pwd", self.admin_password, "-client_certdb_pwd", self.admin_password,
"-preop_pin" , preop_pin, "-preop_pin" , preop_pin,
@@ -484,10 +505,9 @@ class CAInstance(service.Service):
"-ca_server_cert_subject_name", "CN=" + self.host_name + ",O=" + self.domain_name, "-ca_server_cert_subject_name", "CN=" + self.host_name + ",O=" + self.domain_name,
"-ca_audit_signing_cert_subject_name", "\"CN=CA Audit Signing Certificate,O=" + self.domain_name + "\"", "-ca_audit_signing_cert_subject_name", "\"CN=CA Audit Signing Certificate,O=" + self.domain_name + "\"",
"-ca_sign_cert_subject_name", "\"CN=Certificate Authority,O=" + self.domain_name + "\"" ] "-ca_sign_cert_subject_name", "\"CN=Certificate Authority,O=" + self.domain_name + "\"" ]
# if (options.external): if (self.external):
# pass args.append("-external")
# args.append("-external") args.append("true")
# args.append("true")
# args.append("-ext_csr_file") # args.append("-ext_csr_file")
# args.append(ext_csr_file) # args.append(ext_csr_file)
# if (options.cacertfile): # if (options.cacertfile):
@@ -496,39 +516,51 @@ class CAInstance(service.Service):
# if (options.cacertchainfile): # if (options.cacertchainfile):
# args.append("-ext_ca_cert_chain_file") # args.append("-ext_ca_cert_chain_file")
# args.append(options.cacertchainfile) # args.append(options.cacertchainfile)
# else: else:
# args.append("-external") args.append("-external")
# args.append("false") args.append("false")
# if (options.clone): if (self.clone):
# pass """sd = security domain --> all CS systems get registered to
# args.append("-clone") a security domain. This is set to the hostname and port of
# args.append("true") the master CA.
# args.append("-clone_p12_file") """
# args.append(options.clonefile) # The install wizard expects the file to be here.
# args.append("-clone_p12_password") cafile = self.pkcs12_info[0]
# args.append(options.clonepasswd) shutil.copy(cafile, "/var/lib/pki-ca/alias/ca.p12")
# args.append("-clone_uri") pent = pwd.getpwnam(self.pki_user)
# args.append(options.cloneURI) os.chown("/var/lib/pki-ca/alias/ca.p12", pent.pw_uid, pent.pw_gid )
# args.append("-sd_hostname") args.append("-clone")
# args.append(options.sd_hostname) args.append("true")
# args.append("-sd_ssl_port") args.append("-clone_p12_file")
# args.append(options.sd_ssl_port) args.append("ca.p12")
# args.append("-sd_admin_name") args.append("-clone_p12_password")
# args.append(options.sd_admin_name) args.append(self.dm_password)
# args.append("-sd_admin_password") args.append("-sd_hostname")
# args.append(options.sd_admin_password) args.append(self.master_host)
# else: args.append("-sd_admin_port")
# args.append("-clone") args.append(str(ADMIN_SECURE_PORT))
# args.append("false") args.append("-sd_admin_name")
args.append("admin")
# FIXME args.append("-sd_admin_password")
args.append("-external") args.append(self.admin_password)
args.append("false") else:
args.append("-clone") args.append("-clone")
args.append("false") args.append("false")
logging.debug(args) logging.debug(args)
ipautil.run(args) ipautil.run(args)
# pkisilent doesn't return 1 on error so look at the output of
# /sbin/service pki-ca status. It will tell us if the instance
# still needs to be configured.
(stdout, stderr) = ipautil.run(["/sbin/service", "pki-ca", "status"])
try:
stdout.index("CONFIGURED!")
raise RuntimeError("pkisilent failed to configure instance.")
except ValueError:
# This is raised because the string doesn't exist, we're done
pass
logging.debug("completed creating ca instance") logging.debug("completed creating ca instance")
except ipautil.CalledProcessError, e: except ipautil.CalledProcessError, e:
logging.critical("failed to restart ca instance %s" % e) logging.critical("failed to restart ca instance %s" % e)
@@ -581,7 +613,7 @@ class CAInstance(service.Service):
'-p', self.admin_password, '-p', self.admin_password,
'-d', self.ca_agent_db, '-d', self.ca_agent_db,
'-r', '/ca/agent/ca/profileReview?requestId=%s' % self.requestId, '-r', '/ca/agent/ca/profileReview?requestId=%s' % self.requestId,
'%s:%d' % (self.host_name, 9443), '%s:%d' % (self.host_name, AGENT_SECURE_PORT),
] ]
logging.debug("running sslget %s" % args) logging.debug("running sslget %s" % args)
(stdout, stderr) = ipautil.run(args) (stdout, stderr) = ipautil.run(args)
@@ -604,7 +636,7 @@ class CAInstance(service.Service):
'-d', self.ca_agent_db, '-d', self.ca_agent_db,
'-e', params, '-e', params,
'-r', '/ca/agent/ca/profileProcess', '-r', '/ca/agent/ca/profileProcess',
'%s:%d' % (self.host_name, 9443), '%s:%d' % (self.host_name, AGENT_SECURE_PORT),
] ]
logging.debug("running sslget %s" % args) logging.debug("running sslget %s" % args)
(stdout, stderr) = ipautil.run(args) (stdout, stderr) = ipautil.run(args)
@@ -629,6 +661,22 @@ class CAInstance(service.Service):
finally: finally:
os.remove(agent_name) os.remove(agent_name)
def import_ra_cert(self, rafile):
"""
Cloned RAs will use the same RA agent cert as the master so we
need to import from a PKCS#12 file.
Used when setting up replication
"""
# Add the new RA cert to the database in /etc/httpd/alias
(agent_fd, agent_name) = tempfile.mkstemp()
os.write(agent_fd, self.dm_password)
os.close(agent_fd)
try:
import_pkcs12(rafile, agent_name, self.ra_agent_db, self.ra_agent_pwd)
finally:
os.remove(agent_name)
def __configure_ra(self): def __configure_ra(self):
# Create an RA user in the CA LDAP server and add that user to # Create an RA user in the CA LDAP server and add that user to
# the appropriate groups so it can issue certificates without # the appropriate groups so it can issue certificates without
@@ -692,9 +740,9 @@ class CAInstance(service.Service):
def __get_ca_chain(self): def __get_ca_chain(self):
try: try:
return dogtag.get_ca_certchain() return dogtag.get_ca_certchain(ca_host=self.host_name)
except: except Exception, e:
raise RuntimeError("Unable to retrieve CA chain") raise RuntimeError("Unable to retrieve CA chain: %s" % str(e))
def __create_ca_agent_pkcs12(self): def __create_ca_agent_pkcs12(self):
(pwd_fd, pwd_name) = tempfile.mkstemp() (pwd_fd, pwd_name) = tempfile.mkstemp()
@@ -766,7 +814,7 @@ class CAInstance(service.Service):
conn.close() conn.close()
raise RuntimeError("Unable to submit RA cert request") raise RuntimeError("Unable to submit RA cert request")
def __fix_ra_perms(self): def fix_ra_perms(self):
os.chmod(self.ra_agent_db + "/cert8.db", 0640) os.chmod(self.ra_agent_db + "/cert8.db", 0640)
os.chmod(self.ra_agent_db + "/key3.db", 0640) os.chmod(self.ra_agent_db + "/key3.db", 0640)
os.chmod(self.ra_agent_db + "/secmod.db", 0640) os.chmod(self.ra_agent_db + "/secmod.db", 0640)
@@ -775,23 +823,13 @@ class CAInstance(service.Service):
os.chown(self.ra_agent_db + "/cert8.db", 0, pent.pw_gid ) os.chown(self.ra_agent_db + "/cert8.db", 0, pent.pw_gid )
os.chown(self.ra_agent_db + "/key3.db", 0, pent.pw_gid ) os.chown(self.ra_agent_db + "/key3.db", 0, pent.pw_gid )
os.chown(self.ra_agent_db + "/secmod.db", 0, pent.pw_gid ) os.chown(self.ra_agent_db + "/secmod.db", 0, pent.pw_gid )
os.chown(self.ra_agent_pwd, 0, pent.pw_gid) os.chown(self.ra_agent_pwd, pent.pw_uid, pent.pw_gid)
def __setup_sign_profile(self): def __setup_sign_profile(self):
caconfig = "/var/lib/pki-ca/conf/CS.cfg" caconfig = "/var/lib/pki-ca/conf/CS.cfg"
if not ipautil.file_exists('/var/lib/pki-ca/profiles/ca/caJarSigningCert.cfg'): # Tell the profile to automatically issue certs for RAs
profile = ipautil.template_file(ipautil.SHARE_DIR + "caJarSigningCert.cfg.template", {}) installutils.set_directive('/var/lib/pki-ca/profiles/ca/caJarSigningCert.cfg', 'auth.instance_id', 'raCertAuth', quotes=False, separator='=')
fd = open("/var/lib/pki-ca/profiles/ca/caJarSigningCert.cfg", "w")
fd.write(profile)
fd.close()
profilelist = installutils.get_directive(caconfig, "profile.list", separator="=")
if profilelist.find('caJarSigningCert') < 0:
profilelist = profilelist + ',caJarSigningCert'
installutils.set_directive(caconfig, 'profile.list', profilelist, quotes=False, separator='=')
installutils.set_directive(caconfig, 'profile.caJarSigningCert.class_id', 'caEnrollImpl', quotes=False, separator='=')
installutils.set_directive(caconfig, 'profile.caJarSigningCert.config', '/var/lib/pki-ca/profiles/ca/caJarSigningCert.cfg', quotes=False, separator='=')
def uninstall(self): def uninstall(self):
try: try:
@@ -803,6 +841,6 @@ class CAInstance(service.Service):
if __name__ == "__main__": if __name__ == "__main__":
installutils.standard_logging_setup("install.log", False) installutils.standard_logging_setup("install.log", False)
cs = CADSInstance() cs = CADSInstance()
cs.create_instance("dirsrv", "GREYOAK.COM", "catest.greyoak.com", "greyoak.com", "password") cs.create_instance("dirsrv", "EXAMPLE.COM", "catest.example.com", "example.com", "password")
ca = CAInstance() ca = CAInstance()
ca.configure_instance("pkiuser", "catest.greyoak.com", "password", "password") ca.configure_instance("pkiuser", "catest.example.com", "password", "password")

View File

@@ -37,6 +37,20 @@ import nss.nss as nss
CA_SERIALNO="/var/lib/ipa/ca_serialno" CA_SERIALNO="/var/lib/ipa/ca_serialno"
def ipa_self_signed():
"""
Determine if the current IPA CA is self-signed or using another CA
Note that this doesn't distinguish between dogtag and being provided
PKCS#12 files from another CA.
A server is self-signed if /var/lib/ipa/ca_serialno exists
"""
if ipautil.file_exists(CA_SERIALNO):
return True
else:
return False
def client_auth_data_callback(ca_names, chosen_nickname, password, certdb): def client_auth_data_callback(ca_names, chosen_nickname, password, certdb):
cert = None cert = None
if chosen_nickname: if chosen_nickname:
@@ -79,10 +93,12 @@ class CertDB(object):
self.certder_fname = self.reqdir + "/tmpcert.der" self.certder_fname = self.reqdir + "/tmpcert.der"
self.host_name = host_name self.host_name = host_name
if ipautil.file_exists(CA_SERIALNO): self.self_signed_ca = ipa_self_signed()
self.self_signed_ca = True
if self.self_signed_ca:
self.subject_format = "CN=%s,ou=test-ipa,O=IPA"
else: else:
self.self_signed_ca = False self.subject_format = "CN=%s,OU=pki-ipa,O=IPA"
# Making this a starting value that will generate # Making this a starting value that will generate
# unique values for the current DB is the # unique values for the current DB is the
@@ -222,6 +238,42 @@ class CertDB(object):
"-f", self.passwd_fname]) "-f", self.passwd_fname])
self.set_perms(self.passwd_fname, write=True) self.set_perms(self.passwd_fname, write=True)
def list_certs(self):
"""
Return a tuple of tuples containing (nickname, trust)
"""
p = subprocess.Popen(["/usr/bin/certutil", "-d", self.secdir,
"-L"], stdout=subprocess.PIPE)
certs = p.stdout.read()
certs = certs.split("\n")
# FIXME, this relies on NSS never changing the formatting of certutil
certlist = []
for cert in certs:
nickname = cert[0:61]
trust = cert[61:]
if re.match(r'\w+,\w+,\w+', trust):
certlist.append((nickname.strip(), trust))
return tuple(certlist)
def has_nickname(self, nickname):
"""
Returns True if nickname exists in the certdb, False otherwise.
This could also be done directly with:
certutil -L -d -n <nickname> ...
"""
certs = self.list_certs()
for cert in certs:
if nickname == cert[0]:
return True
return False
def create_ca_cert(self): def create_ca_cert(self):
# Generate the encryption key # Generate the encryption key
self.run_certutil(["-G", "-z", self.noise_fname, "-f", self.passwd_fname]) self.run_certutil(["-G", "-z", self.noise_fname, "-f", self.passwd_fname])
@@ -279,7 +331,7 @@ class CertDB(object):
raise RuntimeError("Unable to find serial number") raise RuntimeError("Unable to find serial number")
def create_server_cert(self, nickname, subject, other_certdb=None): def create_server_cert(self, nickname, hostname, other_certdb=None, subject=None):
""" """
other_certdb can mean one of two things, depending on the context. other_certdb can mean one of two things, depending on the context.
@@ -288,20 +340,26 @@ class CertDB(object):
If we are using a dogtag CA then it contains the RA agent key If we are using a dogtag CA then it contains the RA agent key
that will issue our cert. that will issue our cert.
You can override the certificate Subject by specifying a subject.
""" """
cdb = other_certdb cdb = other_certdb
if not cdb: if not cdb:
cdb = self cdb = self
if subject is None:
subject=self.subject_format % hostname
(out, err) = self.request_cert(subject) (out, err) = self.request_cert(subject)
cdb.issue_server_cert(self.certreq_fname, self.certder_fname) cdb.issue_server_cert(self.certreq_fname, self.certder_fname)
self.add_cert(self.certder_fname, nickname) self.add_cert(self.certder_fname, nickname)
os.unlink(self.certreq_fname) os.unlink(self.certreq_fname)
os.unlink(self.certder_fname) os.unlink(self.certder_fname)
def create_signing_cert(self, nickname, subject, other_certdb=None): def create_signing_cert(self, nickname, hostname, other_certdb=None, subject=None):
cdb = other_certdb cdb = other_certdb
if not cdb: if not cdb:
cdb = self cdb = self
if subject is None:
subject=self.subject_format % hostname
self.request_cert(subject) self.request_cert(subject)
cdb.issue_signing_cert(self.certreq_fname, self.certder_fname) cdb.issue_signing_cert(self.certreq_fname, self.certder_fname)
self.add_cert(self.certder_fname, nickname) self.add_cert(self.certder_fname, nickname)
@@ -625,6 +683,9 @@ class CertDB(object):
pkcs12_pwd_fname: the file containing the pin for the PKCS#12 file pkcs12_pwd_fname: the file containing the pin for the PKCS#12 file
nickname: the nickname/friendly-name of the cert we are loading nickname: the nickname/friendly-name of the cert we are loading
passwd: The password to use for the new NSS database we are creating passwd: The password to use for the new NSS database we are creating
The global CA may be added as well in case it wasn't included in the
PKCS#12 file. Extra certs won't hurt in any case.
""" """
self.create_noise_file() self.create_noise_file()
self.create_passwd_file(passwd) self.create_passwd_file(passwd)
@@ -638,6 +699,15 @@ class CertDB(object):
nickname = server_certs[0][0] nickname = server_certs[0][0]
self.cacert_name = self.find_root_cert(nickname) self.cacert_name = self.find_root_cert(nickname)
# The point here is to list the cert chain to determine which CA
# to trust. If we get the same nickname back as our server cert
# go ahead and try to pull in the CA in case it either wasn't in the
# PKCS#12 file we loaded or isn't showing in the chain from
# certutil -O (bug #509132)
if self.cacert_name == nickname:
self.cacert_name="CA certificate"
self.load_cacert("/usr/share/ipa/html/ca.crt")
self.trust_root_cert(nickname) self.trust_root_cert(nickname)
self.create_pin_file() self.create_pin_file()
self.export_ca_cert(self.cacert_name, False) self.export_ca_cert(self.cacert_name, False)

View File

@@ -327,12 +327,12 @@ class DsInstance(service.Service):
nickname = "Server-Cert" nickname = "Server-Cert"
if self.self_signed_ca: if self.self_signed_ca:
dsdb.create_self_signed() dsdb.create_self_signed()
dsdb.create_server_cert("Server-Cert", "cn=%s,ou=Fedora Directory Server" % self.host_name) dsdb.create_server_cert("Server-Cert", self.host_name)
else: else:
cadb = certs.CertDB("/etc/httpd/alias", host_name=self.host_name) cadb = certs.CertDB("/etc/httpd/alias", host_name=self.host_name)
cadb.export_ca_cert(cadb.cacert_name, False) cadb.export_ca_cert(cadb.cacert_name, False)
dsdb.create_from_cacert(cadb.cacert_fname, passwd=None) dsdb.create_from_cacert(cadb.cacert_fname, passwd=None)
dsdb.create_server_cert("Server-Cert", "CN=%s,OU=pki-ipa,O=IPA" % self.host_name, cadb) dsdb.create_server_cert("Server-Cert", self.host_name, cadb)
dsdb.create_pin_file() dsdb.create_pin_file()
conn = ipaldap.IPAdmin("127.0.0.1") conn = ipaldap.IPAdmin("127.0.0.1")

View File

@@ -179,11 +179,11 @@ class HTTPInstance(service.Service):
else: else:
if self.self_signed_ca: if self.self_signed_ca:
db.create_from_cacert(ca_db.cacert_fname) db.create_from_cacert(ca_db.cacert_fname)
db.create_server_cert("Server-Cert", "cn=%s,ou=Apache Web Server" % self.fqdn, ca_db) db.create_server_cert("Server-Cert", self.fqdn, ca_db)
db.create_signing_cert("Signing-Cert", "cn=%s,ou=Signing Certificate,o=Identity Policy Audit" % self.fqdn, ca_db) db.create_signing_cert("Signing-Cert", "Object Signing Cert", ca_db)
else: else:
db.create_server_cert("Server-Cert", "CN=%s,OU=ipa-pki,O=IPA" % self.fqdn, ca_db) db.create_server_cert("Server-Cert", self.fqdn, ca_db)
db.create_signing_cert("Signing-Cert", "CN=Object Signing Cert,OU=ipa-pki,O=IPA", ca_db) db.create_signing_cert("Signing-Cert", "Object Signing Cert", ca_db)
db.create_password_conf() db.create_password_conf()
# Fix the database permissions # Fix the database permissions