diff options
author | Rob Crittenden <rcritten@redhat.com> | 2009-07-10 16:18:16 -0400 |
---|---|---|
committer | Rob Crittenden <rcritten@redhat.com> | 2009-07-15 09:00:01 -0400 |
commit | 8d164569d0e4ee79089ae224ac6f5a569c291cdb (patch) | |
tree | a75db1b23693315d1b35bad891ea6c86019d7149 /ipaserver | |
parent | 904e55540438cfd88507fa747daa585605b90bdb (diff) | |
download | freeipa-8d164569d0e4ee79089ae224ac6f5a569c291cdb.tar.gz freeipa-8d164569d0e4ee79089ae224ac6f5a569c291cdb.tar.xz freeipa-8d164569d0e4ee79089ae224ac6f5a569c291cdb.zip |
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.
Diffstat (limited to 'ipaserver')
-rw-r--r-- | ipaserver/install/cainstance.py | 202 | ||||
-rw-r--r-- | ipaserver/install/certs.py | 80 | ||||
-rw-r--r-- | ipaserver/install/dsinstance.py | 4 | ||||
-rw-r--r-- | ipaserver/install/httpinstance.py | 8 |
4 files changed, 201 insertions, 93 deletions
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py index 264f9b9f..a8a1a774 100644 --- a/ipaserver/install/cainstance.py +++ b/ipaserver/install/cainstance.py @@ -51,6 +51,14 @@ from ipalib import util 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 # information INF_TEMPLATE = """ @@ -88,13 +96,12 @@ def get_preop_pin(instance_root, instance_name): return preop_pin -def export_pkcs12(output_file, output_passwd, nickname, cert_database, +def import_pkcs12(input_file, input_passwd, cert_database, cert_passwd): ipautil.run(["/usr/bin/pk12util", "-d", cert_database, - "-o", output_file, - "-n", nickname, + "-i", input_file, "-k", cert_passwd, - "-w", output_passwd]) + "-w", input_passwd]) def client_auth_data_callback(ca_names, chosen_nickname, password, certdb): cert = None @@ -147,7 +154,7 @@ def get_defList(data): """ 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 value = None @@ -215,6 +222,7 @@ class CADSInstance(service.Service): self.pkcs12_info = None self.ds_user = None self.ds_port = None + self.master_host = None if realm_name: self.suffix = util.realm_to_suffix(self.realm_name) self.__setup_sub_dict() @@ -351,10 +359,13 @@ class CAInstance(service.Service): def __init__(self): service.Service.__init__(self, "pki-ca") - self.pki_user = None + self.pki_user = "pkiuser" self.dm_password = None self.admin_password = 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 # will already have been initialized by Apache by the time @@ -367,31 +378,36 @@ class CAInstance(service.Service): self.ds_port = DEFAULT_DSPORT self.domain_name = "IPA" self.server_root = "/var/lib" - self.secure_port = 9444 self.ra_cert = None self.requestId = None def __del__(self): 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.host_name = host_name self.dm_password = dm_password self.admin_password = admin_password 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"): self.step("creating pki-ca instance", self.create_instance) self.step("creating certificate server user", self.__create_ca_user) 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("importing CA chain to RA certificate database", self.__import_ca_chain) - self.step("requesting RA certificate from CA", self.__request_ra_certificate) - self.step("issuing RA agent certificate", self.__issue_ra_cert) - self.step("adding RA agent as a trusted user", self.__configure_ra) - self.step("fixing RA database permissions", self.__fix_ra_perms) + if not self.clone: + self.step("requesting RA certificate from CA", self.__request_ra_certificate) + self.step("issuing RA agent certificate", self.__issue_ra_cert) + 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("configuring certificate server to start on boot", self.__enable) self.step("restarting certificate server", self.__restart_instance) @@ -401,30 +417,35 @@ class CAInstance(service.Service): def create_instance(self): """ 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', '-pki_instance_root', '/var/lib', '-pki_instance_name', PKI_INSTANCE_NAME, '-subsystem_type', 'ca', - '-agent_secure_port', AGENT_SECURE_PORT, - '-ee_secure_port', EE_SECURE_PORT, - '-admin_secure_port', ADMIN_SECURE_PORT, - '-unsecure_port', UNSECURE_PORT, - '-tomcat_server_port', TOMCAT_SERVER_PORT, + '-agent_secure_port', str(AGENT_SECURE_PORT), + '-ee_secure_port', str(EE_SECURE_PORT), + '-admin_secure_port', str(ADMIN_SECURE_PORT), + '-unsecure_port', str(UNSECURE_PORT), + '-tomcat_server_port', str(TOMCAT_SERVER_PORT), '-redirect', 'conf=/etc/pki-ca', '-redirect', 'logs=/var/log/pki-ca', ] 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): self.backup_state("enabled", self.is_enabled()) self.chkconfig_on() @@ -455,7 +476,7 @@ class CAInstance(service.Service): try: args = ["/usr/bin/perl", "/usr/bin/pkisilent", "ConfigureCA", "-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_pwd", self.admin_password, "-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_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 + "\"" ] -# if (options.external): -# pass -# args.append("-external") -# args.append("true") + if (self.external): + args.append("-external") + args.append("true") # args.append("-ext_csr_file") # args.append(ext_csr_file) # if (options.cacertfile): @@ -496,39 +516,51 @@ class CAInstance(service.Service): # if (options.cacertchainfile): # args.append("-ext_ca_cert_chain_file") # args.append(options.cacertchainfile) -# else: -# args.append("-external") -# args.append("false") -# if (options.clone): -# pass -# args.append("-clone") -# args.append("true") -# args.append("-clone_p12_file") -# args.append(options.clonefile) -# args.append("-clone_p12_password") -# args.append(options.clonepasswd) -# args.append("-clone_uri") -# args.append(options.cloneURI) -# args.append("-sd_hostname") -# args.append(options.sd_hostname) -# args.append("-sd_ssl_port") -# args.append(options.sd_ssl_port) -# args.append("-sd_admin_name") -# args.append(options.sd_admin_name) -# args.append("-sd_admin_password") -# args.append(options.sd_admin_password) -# else: -# args.append("-clone") -# args.append("false") - - # FIXME - args.append("-external") - args.append("false") - args.append("-clone") - args.append("false") + else: + args.append("-external") + args.append("false") + if (self.clone): + """sd = security domain --> all CS systems get registered to + a security domain. This is set to the hostname and port of + the master CA. + """ + # The install wizard expects the file to be here. + cafile = self.pkcs12_info[0] + shutil.copy(cafile, "/var/lib/pki-ca/alias/ca.p12") + pent = pwd.getpwnam(self.pki_user) + os.chown("/var/lib/pki-ca/alias/ca.p12", pent.pw_uid, pent.pw_gid ) + args.append("-clone") + args.append("true") + args.append("-clone_p12_file") + args.append("ca.p12") + args.append("-clone_p12_password") + args.append(self.dm_password) + args.append("-sd_hostname") + args.append(self.master_host) + args.append("-sd_admin_port") + args.append(str(ADMIN_SECURE_PORT)) + args.append("-sd_admin_name") + args.append("admin") + args.append("-sd_admin_password") + args.append(self.admin_password) + else: + args.append("-clone") + args.append("false") logging.debug(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") except ipautil.CalledProcessError, e: logging.critical("failed to restart ca instance %s" % e) @@ -581,7 +613,7 @@ class CAInstance(service.Service): '-p', self.admin_password, '-d', self.ca_agent_db, '-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) (stdout, stderr) = ipautil.run(args) @@ -604,7 +636,7 @@ class CAInstance(service.Service): '-d', self.ca_agent_db, '-e', params, '-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) (stdout, stderr) = ipautil.run(args) @@ -629,6 +661,22 @@ class CAInstance(service.Service): finally: 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): # Create an RA user in the CA LDAP server and add that user to # the appropriate groups so it can issue certificates without @@ -692,9 +740,9 @@ class CAInstance(service.Service): def __get_ca_chain(self): try: - return dogtag.get_ca_certchain() - except: - raise RuntimeError("Unable to retrieve CA chain") + return dogtag.get_ca_certchain(ca_host=self.host_name) + except Exception, e: + raise RuntimeError("Unable to retrieve CA chain: %s" % str(e)) def __create_ca_agent_pkcs12(self): (pwd_fd, pwd_name) = tempfile.mkstemp() @@ -766,7 +814,7 @@ class CAInstance(service.Service): conn.close() 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 + "/key3.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 + "/key3.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): caconfig = "/var/lib/pki-ca/conf/CS.cfg" - if not ipautil.file_exists('/var/lib/pki-ca/profiles/ca/caJarSigningCert.cfg'): - profile = ipautil.template_file(ipautil.SHARE_DIR + "caJarSigningCert.cfg.template", {}) - 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='=') + # Tell the profile to automatically issue certs for RAs + installutils.set_directive('/var/lib/pki-ca/profiles/ca/caJarSigningCert.cfg', 'auth.instance_id', 'raCertAuth', quotes=False, separator='=') def uninstall(self): try: @@ -803,6 +841,6 @@ class CAInstance(service.Service): if __name__ == "__main__": installutils.standard_logging_setup("install.log", False) 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.configure_instance("pkiuser", "catest.greyoak.com", "password", "password") + ca.configure_instance("pkiuser", "catest.example.com", "password", "password") diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py index bad9a1e6..7a4a3129 100644 --- a/ipaserver/install/certs.py +++ b/ipaserver/install/certs.py @@ -37,6 +37,20 @@ import nss.nss as nss 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): cert = None if chosen_nickname: @@ -79,10 +93,12 @@ class CertDB(object): self.certder_fname = self.reqdir + "/tmpcert.der" self.host_name = host_name - if ipautil.file_exists(CA_SERIALNO): - self.self_signed_ca = True + self.self_signed_ca = ipa_self_signed() + + if self.self_signed_ca: + self.subject_format = "CN=%s,ou=test-ipa,O=IPA" else: - self.self_signed_ca = False + self.subject_format = "CN=%s,OU=pki-ipa,O=IPA" # Making this a starting value that will generate # unique values for the current DB is the @@ -222,6 +238,42 @@ class CertDB(object): "-f", self.passwd_fname]) 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): # Generate the encryption key 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") - 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. @@ -288,20 +340,26 @@ class CertDB(object): If we are using a dogtag CA then it contains the RA agent key that will issue our cert. + + You can override the certificate Subject by specifying a subject. """ cdb = other_certdb if not cdb: cdb = self + if subject is None: + subject=self.subject_format % hostname (out, err) = self.request_cert(subject) cdb.issue_server_cert(self.certreq_fname, self.certder_fname) self.add_cert(self.certder_fname, nickname) os.unlink(self.certreq_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 if not cdb: cdb = self + if subject is None: + subject=self.subject_format % hostname self.request_cert(subject) cdb.issue_signing_cert(self.certreq_fname, self.certder_fname) 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 nickname: the nickname/friendly-name of the cert we are loading 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_passwd_file(passwd) @@ -638,6 +699,15 @@ class CertDB(object): nickname = server_certs[0][0] 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.create_pin_file() self.export_ca_cert(self.cacert_name, False) diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py index e31cd081..76eb8dd9 100644 --- a/ipaserver/install/dsinstance.py +++ b/ipaserver/install/dsinstance.py @@ -327,12 +327,12 @@ class DsInstance(service.Service): nickname = "Server-Cert" if self.self_signed_ca: 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: cadb = certs.CertDB("/etc/httpd/alias", host_name=self.host_name) cadb.export_ca_cert(cadb.cacert_name, False) 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() conn = ipaldap.IPAdmin("127.0.0.1") diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py index 1bd9ffd5..bf926737 100644 --- a/ipaserver/install/httpinstance.py +++ b/ipaserver/install/httpinstance.py @@ -179,11 +179,11 @@ class HTTPInstance(service.Service): else: if self.self_signed_ca: 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_signing_cert("Signing-Cert", "cn=%s,ou=Signing Certificate,o=Identity Policy Audit" % self.fqdn, ca_db) + db.create_server_cert("Server-Cert", self.fqdn, ca_db) + db.create_signing_cert("Signing-Cert", "Object Signing Cert", ca_db) else: - db.create_server_cert("Server-Cert", "CN=%s,OU=ipa-pki,O=IPA" % self.fqdn, ca_db) - db.create_signing_cert("Signing-Cert", "CN=Object Signing Cert,OU=ipa-pki,O=IPA", ca_db) + db.create_server_cert("Server-Cert", self.fqdn, ca_db) + db.create_signing_cert("Signing-Cert", "Object Signing Cert", ca_db) db.create_password_conf() # Fix the database permissions |