diff options
Diffstat (limited to 'ipaserver/install/krainstance.py')
-rw-r--r-- | ipaserver/install/krainstance.py | 346 |
1 files changed, 346 insertions, 0 deletions
diff --git a/ipaserver/install/krainstance.py b/ipaserver/install/krainstance.py new file mode 100644 index 000000000..182e8e034 --- /dev/null +++ b/ipaserver/install/krainstance.py @@ -0,0 +1,346 @@ +# Authors: Ade Lee <alee@redhat.com> +# +# Copyright (C) 2014 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +import ConfigParser +import os +import pwd +import shutil +import sys +import tempfile + +from ipalib import api +from ipaplatform import services +from ipaplatform.paths import paths +from ipapython import dogtag +from ipapython import ipaldap +from ipapython import ipautil +from ipapython.dn import DN +from ipaserver.install import certs +from ipaserver.install import cainstance +from ipaserver.install import service +from ipaserver.install.dogtaginstance import DogtagInstance +from ipaserver.install.dogtaginstance import DEFAULT_DSPORT, PKI_USER +from ipapython.ipa_log_manager import log_mgr + +# When IPA is installed with DNS support, this CNAME should hold all IPA +# replicas with KRA configured +IPA_KRA_RECORD = "ipa-kra" + + +class KRAInstance(DogtagInstance): + """ + We assume that the CA has already been installed, and we use the + same tomcat instance to host both the CA and KRA. + The mod_nss database will contain the RA agent cert that will be used + to do authenticated requests against dogtag. The RA agent cert will + be the same for both the CA and KRA. + """ + + def __init__(self, realm, dogtag_constants=None): + if dogtag_constants is None: + dogtag_constants = dogtag.configured_constants() + + super(KRAInstance, self).__init__( + realm=realm, + subsystem="KRA", + service_desc="KRA server", + dogtag_constants=dogtag_constants + ) + + self.basedn = DN(('o', 'kra'), ('o', 'ipaca')) + self.tracking_reqs = (('auditSigningCert cert-pki-kra', None), + ('transportCert cert-pki-kra', None), + ('storageCert cert-pki-kra', None)) + self.log = log_mgr.get_logger(self) + + def configure_instance(self, host_name, domain, dm_password, + admin_password, ds_port=DEFAULT_DSPORT, + pkcs12_info=None, master_host=None, + master_replication_port=None, + subject_base=None): + """Create a KRA instance. + + To create a clone, pass in pkcs12_info. + """ + self.fqdn = host_name + self.domain = domain + 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 + self.master_replication_port = master_replication_port + if subject_base is None: + self.subject_base = DN(('O', self.realm)) + else: + self.subject_base = subject_base + + # Confirm that a KRA does not already exist + if self.is_installed(): + raise RuntimeError( + "KRA already installed.") + # Confirm that a Dogtag 10 CA instance already exists + ca = cainstance.CAInstance( + api.env.realm, certs.NSS_DIR, + dogtag_constants=dogtag.Dogtag10Constants) + if not ca.is_installed(): + raise RuntimeError( + "KRA configuration failed. " + "A Dogtag CA must be installed first") + + self.step("configuring KRA instance", self.__spawn_instance) + if not self.clone: + self.step("add RA user to KRA agent group", + self.__add_ra_user_to_agent_group) + self.step("restarting KRA", self.restart_instance) + self.step("configure certificate renewals", self.configure_renewal) + self.step("Configure HTTP to proxy connections", + self.http_proxy) + + self.start_creation(runtime=126) + + def __spawn_instance(self): + """ + Create and configure a new KRA instance using pkispawn. + Creates a configuration file with IPA-specific + parameters and passes it to the base class to call pkispawn + """ + + # Create an empty and secured file + (cfg_fd, cfg_file) = tempfile.mkstemp() + os.close(cfg_fd) + pent = pwd.getpwnam(PKI_USER) + os.chown(cfg_file, pent.pw_uid, pent.pw_gid) + + # Create KRA configuration + config = ConfigParser.ConfigParser() + config.optionxform = str + config.add_section("KRA") + + # Security Domain Authentication + config.set("KRA", "pki_security_domain_https_port", "443") + config.set("KRA", "pki_security_domain_password", self.admin_password) + config.set("KRA", "pki_security_domain_user", "admin") + + # issuing ca + config.set("KRA", "pki_issuing_ca_uri", "https://%s" % + ipautil.format_netloc(self.fqdn, 443)) + + # Server + config.set("KRA", "pki_enable_proxy", "True") + config.set("KRA", "pki_restart_configured_instance", "False") + config.set("KRA", "pki_backup_keys", "True") + config.set("KRA", "pki_backup_password", self.admin_password) + + # Client security database + config.set("KRA", "pki_client_database_dir", self.agent_db) + config.set("KRA", "pki_client_database_password", self.admin_password) + config.set("KRA", "pki_client_database_purge", "False") + config.set("KRA", "pki_client_pkcs12_password", self.admin_password) + + # Administrator + config.set("KRA", "pki_admin_name", "admin") + config.set("KRA", "pki_admin_uid", "admin") + config.set("KRA", "pki_admin_email", "root@localhost") + config.set("KRA", "pki_admin_password", self.admin_password) + config.set("KRA", "pki_admin_nickname", "ipa-ca-agent") + config.set("KRA", "pki_admin_subject_dn", + str(DN(('cn', 'ipa-ca-agent'), self.subject_base))) + config.set("KRA", "pki_import_admin_cert", "True") + config.set("KRA", "pki_admin_cert_file", paths.ADMIN_CERT_PATH) + config.set("KRA", "pki_client_admin_cert_p12", paths.DOGTAG_AGENT_P12) + + # Directory server + config.set("KRA", "pki_ds_ldap_port", str(self.ds_port)) + config.set("KRA", "pki_ds_password", self.dm_password) + config.set("KRA", "pki_ds_base_dn", self.basedn) + config.set("KRA", "pki_ds_database", "ipaca") + config.set("KRA", "pki_ds_create_new_db", "False") + + # Certificate subject DNs + config.set("KRA", "pki_subsystem_subject_dn", + str(DN(('cn', 'CA Subsystem'), self.subject_base))) + config.set("KRA", "pki_ssl_server_subject_dn", + str(DN(('cn', self.fqdn), self.subject_base))) + config.set("KRA", "pki_audit_signing_subject_dn", + str(DN(('cn', 'KRA Audit'), self.subject_base))) + config.set( + "KRA", "pki_transport_subject_dn", + str(DN(('cn', 'KRA Transport Certificate'), self.subject_base))) + config.set( + "KRA", "pki_storage_subject_dn", + str(DN(('cn', 'KRA Storage Certificate'), self.subject_base))) + + # Certificate nicknames + # Note that both the server certs and subsystem certs reuse + # the ca certs. + config.set("KRA", "pki_subsystem_nickname", + "subsystemCert cert-pki-ca") + config.set("KRA", "pki_ssl_server_nickname", + "Server-Cert cert-pki-ca") + config.set("KRA", "pki_audit_signing_nickname", + "auditSigningCert cert-pki-kra") + config.set("KRA", "pki_transport_nickname", + "transportCert cert-pki-kra") + config.set("KRA", "pki_storage_nickname", + "storageCert cert-pki-kra") + + # Shared db settings + # Needed because CA and KRA share the same database + # We will use the dbuser created for the CA + config.set("KRA", "pki_share_db", "True") + config.set( + "KRA", "pki_share_dbuser_dn", + str(DN(('uid', 'pkidbuser'), ('ou', 'people'), ('o', 'ipaca')))) + + _p12_tmpfile_handle, p12_tmpfile_name = tempfile.mkstemp(dir=paths.TMP) + if self.clone: + krafile = self.pkcs12_info[0] + shutil.copy(krafile, p12_tmpfile_name) + pent = pwd.getpwnam(PKI_USER) + os.chown(p12_tmpfile_name, pent.pw_uid, pent.pw_gid) + + # create admin cert file if it does not exist + cert = DogtagInstance.get_admin_cert(self) + with open(paths.ADMIN_CERT_PATH, "w") as admin_path: + admin_path.write(cert) + + # Security domain registration + config.set("KRA", "pki_security_domain_hostname", self.master_host) + config.set("KRA", "pki_security_domain_https_port", "443") + config.set("KRA", "pki_security_domain_user", "admin") + config.set("KRA", "pki_security_domain_password", + self.admin_password) + + # Clone + config.set("KRA", "pki_clone", "True") + config.set("KRA", "pki_clone_pkcs12_path", p12_tmpfile_name) + config.set("KRA", "pki_clone_pkcs12_password", self.dm_password) + config.set("KRA", "pki_clone_setup_replication", "False") + config.set( + "KRA", "pki_clone_uri", + "https://%s" % ipautil.format_netloc(self.master_host, 443)) + + # Generate configuration file + with open(cfg_file, "wb") as f: + config.write(f) + + try: + DogtagInstance.spawn_instance(self, cfg_file) + finally: + os.remove(p12_tmpfile_name) + os.remove(cfg_file) + + shutil.move(paths.KRA_BACKUP_KEYS_P12, paths.KRACERT_P12) + self.log.debug("completed creating KRA instance") + + def __add_ra_user_to_agent_group(self): + """ + Add RA agent created for CA to KRA agent group. + """ + conn = ipaldap.IPAdmin(self.fqdn, self.ds_port) + conn.do_simple_bind(DN(('cn', 'Directory Manager')), self.dm_password) + + entry_dn = DN(('uid', "ipara"), ('ou', 'People'), ('o', 'ipaca')) + dn = DN(('cn', 'Data Recovery Manager Agents'), ('ou', 'groups'), + self.basedn) + modlist = [(0, 'uniqueMember', '%s' % entry_dn)] + conn.modify_s(dn, modlist) + + conn.unbind() + + @staticmethod + def update_cert_config(nickname, cert, dogtag_constants=None): + """ + When renewing a KRA subsystem certificate the configuration file + needs to get the new certificate as well. + + nickname is one of the known nicknames. + cert is a DER-encoded certificate. + """ + + if dogtag_constants is None: + dogtag_constants = dogtag.configured_constants() + + # The cert directive to update per nickname + directives = { + 'auditSigningCert cert-pki-kra': 'kra.audit_signing.cert', + 'storageCert cert-pki-kra': 'kra.storage.cert', + 'transportCert cert-pki-kra': 'kra.transport.cert', + 'subsystemCert cert-pki-kra': 'kra.subsystem.cert', + 'Server-Cert cert-pki-ca': 'kra.sslserver.cert'} + + DogtagInstance.update_cert_cs_cfg( + nickname, cert, directives, + dogtag.configured_constants().KRA_CS_CFG_PATH, + dogtag_constants) + + +def install_replica_kra(config, postinstall=False): + """ + Install a KRA on a replica. + + There are two modes of doing this controlled: + - While the replica is being installed + - Post-replica installation + + config is a ReplicaConfig object + + Returns a KRA instance + """ + # note that the cacert.p12 file is regenerated during the + # ipa-replica-prepare process and should include all the certs + # for the CA and KRA + krafile = config.dir + "/cacert.p12" + + if not ipautil.file_exists(krafile): + raise RuntimeError( + "Unable to clone KRA." + " cacert.p12 file not found in replica file") + + _kra = KRAInstance(config.realm_name, + dogtag_constants=dogtag.install_constants) + _kra.dm_password = config.dirman_password + _kra.subject_base = config.subject_base + if _kra.is_installed(): + sys.exit("A KRA is already configured on this system.") + + _kra.configure_instance(config.host_name, config.domain_name, + config.dirman_password, config.dirman_password, + pkcs12_info=(krafile,), + master_host=config.master_host_name, + master_replication_port=config.ca_ds_port, + subject_base=config.subject_base) + + # Restart httpd since we changed it's config and added ipa-pki-proxy.conf + if postinstall: + services.knownservices.httpd.restart() + + # The dogtag DS instance needs to be restarted after installation. + # The procedure for this is: stop dogtag, stop DS, start DS, start + # dogtag + + service.print_msg("Restarting the directory and KRA servers") + _kra.stop(dogtag.install_constants.PKI_INSTANCE_NAME) + services.knownservices.dirsrv.restart() + _kra.start(dogtag.install_constants.PKI_INSTANCE_NAME) + + return _kra |