diff --git a/install/share/Makefile.am b/install/share/Makefile.am index 881e29123..be83bdf75 100644 --- a/install/share/Makefile.am +++ b/install/share/Makefile.am @@ -96,6 +96,8 @@ dist_app_DATA = \ ipa-rewrite.conf.template \ min-ssf.ldif \ ipaca_default.ini \ + ipaca_customize.ini \ + ipaca_softhsm2.ini \ $(NULL) kdcproxyconfdir = $(IPA_SYSCONF_DIR)/kdcproxy diff --git a/install/share/ipaca_customize.ini b/install/share/ipaca_customize.ini new file mode 100644 index 000000000..9523e7b70 --- /dev/null +++ b/install/share/ipaca_customize.ini @@ -0,0 +1,123 @@ +# +# Dogtag PKI configuration file +# +# Notes: +# - "%" must be quoted as "%%". +# - options in the [CA] and [KRA] section cannot be overriden from options +# in the [DEFAULT] section +# - pki_*_token options are hard-coded to pki_token_name +# - pki_sslserver_token is hard-coded to 'internal' +# - pki_backup_keys is automatically disabled when HSM support is enabled, +# as HSM backup is not possible with the default mechanism. +# +# Predefined variables +# - ipa_ca_subject +# - ipa_ds_base_dn +# - ipa_fqdn +# - ipa_subject_base +# - pki_admin_password +# - pki_dns_domainname +# - softhsm2_so + + +[DEFAULT] +# default algorithms for all certificates +ipa_key_algorithm=SHA256withRSA +ipa_key_size=2048 +ipa_key_type=rsa +ipa_signing_algorithm=SHA256withRSA + +# Used for IPA CA +# signing algorithm can be overriden on command line +ipa_ca_signing_algorithm=%(ipa_key_algorithm)s +ipa_ca_key_size=%(ipa_key_size)s +ipa_ca_key_type=%(ipa_key_type)s + +# HSM support +pki_hsm_enable=False +pki_hsm_libfile= +pki_hsm_modulename= +pki_token_name=internal +# backup is automatically disabled when HSM support is enabled +pki_backup_keys=True +pki_backup_password=%(pki_admin_password)s + +pki_admin_email=root@localhost + +## auditSigningCert cert-pki-ca / auditSigningCert cert-pki-kra +pki_audit_signing_key_algorithm=%(ipa_key_algorithm)s +pki_audit_signing_key_size=%(ipa_key_size)s +pki_audit_signing_key_type=%(ipa_key_type)s +pki_audit_signing_signing_algorithm=%(ipa_signing_algorithm)s +pki_audit_signing_token=%(pki_token_name)s + +# Configures the status request timeout, i.e. the connect/data +# timeout on the HTTP request to get the status of Dogtag. +# +# This configuration is needed in "multiple IP address" scenarios +# where this server's hostname has multiple IP addresses but the +# HTTP server is only listening on one of them. Without a timeout, +# if a "wrong" IP address is tried first, it will take a long time +# to timeout, exceeding the overall timeout hence the request will +# not be re-tried. Setting a shorter timeout allows the request +# to be re-tried. +# +# Note that HSMs cause different behaviour so this value might +# not be suitable for when we implement HSM support. It is +# known that a value of 5s is too short in HSM environment. +# +pki_status_request_timeout=15 + +# for supporting server cert SAN injection +pki_san_inject=False +pki_san_for_server_cert= + +## Server-Cert cert-pki-ca +pki_sslserver_key_algorithm=%(ipa_key_algorithm)s +pki_sslserver_key_size=%(ipa_key_size)s +pki_sslserver_key_type=%(ipa_key_type)s + +## subsystemCert cert-pki-ca +pki_subsystem_key_algorithm=%(ipa_key_algorithm)s +pki_subsystem_key_size=%(ipa_key_size)s +pki_subsystem_key_type=%(ipa_key_type)s +pki_subsystem_token=%(pki_token_name)s + +[CA] +pki_random_serial_numbers_enable=False + +## caSigningCert cert-pki-ca +pki_ca_signing_key_algorithm=%(ipa_ca_signing_algorithm)s +pki_ca_signing_key_size=%(ipa_ca_key_size)s +pki_ca_signing_key_type=%(ipa_ca_key_type)s +pki_ca_signing_signing_algorithm=%(ipa_ca_signing_algorithm)s +pki_ca_signing_token=%(pki_token_name)s + +# MS subca request ext data +pki_req_ext_oid=1.3.6.1.4.1.311.20.2 +pki_req_ext_critical=False +pki_req_ext_data=1E0A00530075006200430041 + +## ocspSigningCert cert-pki-ca +pki_ocsp_signing_key_algorithm=%(ipa_key_algorithm)s +pki_ocsp_signing_key_size=%(ipa_key_size)s +pki_ocsp_signing_key_type=%(ipa_key_type)s +pki_ocsp_signing_signing_algorithm=%(ipa_signing_algorithm)s +pki_ocsp_signing_token=%(pki_token_name)s + +[KRA] +pki_kra_ephemeral_requests=True + +## storageCert cert-pki-kra +pki_storage_key_algorithm=%(ipa_key_algorithm)s +pki_storage_key_size=%(ipa_key_size)s +pki_storage_key_type=%(ipa_key_type)s +pki_storage_signing_algorithm=%(ipa_signing_algorithm)s +pki_storage_token=%(pki_token_name)s + +## transportCert cert-pki-kra +pki_transport_key_algorithm=%(ipa_key_algorithm)s +pki_transport_key_size=%(ipa_key_size)s +pki_transport_key_type=%(ipa_key_type)s +pki_transport_signing_algorithm=%(ipa_signing_algorithm)s +pki_transport_token=%(pki_token_name)s diff --git a/install/share/ipaca_default.ini b/install/share/ipaca_default.ini index 262e1ca75..4003f4293 100644 --- a/install/share/ipaca_default.ini +++ b/install/share/ipaca_default.ini @@ -1,28 +1,16 @@ # # Dogtag PKI configuration file # +# The ipaca_default.ini contains hard-coded defaults that cannot be modified +# by a user without breaking FreeIPA internals. +# # Note: "%" must be quoted as "%%". # [DEFAULT] -ipa_admin_email=root@localhost - -# default algorithms for all certificates -ipa_key_algorithm=SHA256withRSA -ipa_key_size=2048 -ipa_key_type=rsa -ipa_signing_algorithm=SHA256withRSA - -# Used for IPA CA -# signing algorithm can be overriden on command line -ipa_ca_signing_algorithm=%(ipa_key_algorithm)s -ipa_ca_key_size=%(ipa_key_size)s -ipa_ca_key_type=%(ipa_key_type)s - # hard-coded IPA default settings ipa_security_domain_name=IPA ipa_ds_database=ipaca -ipa_admin_user=admin ipa_admin_nickname=ipa-ca-agent ipa_ca_pem_file=/etc/ipa/ca.crt @@ -33,20 +21,11 @@ ipa_ca_pem_file=/etc/ipa/ca.crt # ipa_fqdn= # ipa_ocsp_uri= # ipa_admin_cert_p12= +# ipa_admin_user= # sensitive dynamic values # pki_admin_password= # pki_ds_password= -# pki_token_password= - -# HSM support -pki_hsm_enable=False -pki_hsm_libfile= -pki_hsm_modulename= -pki_token_name=internal -# backup is automatically disabled when HSM support is enabled -pki_backup_keys=True -pki_backup_password=%(pki_admin_password)s # Dogtag defaults pki_instance_name=pki-tomcat @@ -56,16 +35,10 @@ pki_instance_configuration_path=%(pki_configuration_path)s/%(pki_instance_name)s pki_admin_cert_file=%(pki_client_dir)s/ca_admin.cert pki_admin_cert_request_type=pkcs10 pki_admin_dualkey=False -pki_admin_key_algorithm=%(ipa_key_algorithm)s -pki_admin_key_size=%(ipa_key_size)s -pki_admin_key_type=%(ipa_key_type)s - -pki_audit_group=pkiaudit -pki_audit_signing_key_algorithm=%(ipa_key_algorithm)s -pki_audit_signing_key_size=%(ipa_key_size)s -pki_audit_signing_key_type=%(ipa_key_type)s -pki_audit_signing_signing_algorithm=%(ipa_signing_algorithm)s -pki_audit_signing_token=%(pki_token_name)s +pki_admin_name=%(ipa_admin_user)s +pki_admin_nickname=%(ipa_admin_nickname)s +pki_admin_subject_dn=cn=%(ipa_admin_nickname)s,%(ipa_subject_base)s +pki_admin_uid=%(ipa_admin_user)s pki_ca_hostname=%(pki_security_domain_hostname)s pki_ca_port=%(pki_security_domain_https_port)s @@ -86,7 +59,6 @@ pki_ds_remove_data=True pki_ds_secure_connection=False pki_ds_secure_connection_ca_nickname=Directory Server CA certificate pki_ds_secure_connection_ca_pem_file=%(ipa_ca_pem_file)s -pki_group=pkiuser pki_issuing_ca_hostname=%(pki_security_domain_hostname)s pki_issuing_ca_https_port=%(pki_security_domain_https_port)s @@ -94,23 +66,6 @@ pki_issuing_ca_uri=https://%(ipa_fqdn)s:443 pki_issuing_ca=%(pki_issuing_ca_uri)s pki_replication_password= -# Configures the status request timeout, i.e. the connect/data -# timeout on the HTTP request to get the status of Dogtag. -# -# This configuration is needed in "multiple IP address" scenarios -# where this server's hostname has multiple IP addresses but the -# HTTP server is only listening on one of them. Without a timeout, -# if a "wrong" IP address is tried first, it will take a long time -# to timeout, exceeding the overall timeout hence the request will -# not be re-tried. Setting a shorter timeout allows the request -# to be re-tried. -# -# Note that HSMs cause different behaviour so this value might -# not be suitable for when we implement HSM support. It is -# known that a value of 5s is too short in HSM environment. -# -pki_status_request_timeout=15 - pki_enable_proxy=True pki_restart_configured_instance=False pki_security_domain_hostname=%(ipa_fqdn)s @@ -120,32 +75,23 @@ pki_security_domain_password=%(pki_admin_password)s pki_security_domain_user=%(ipa_admin_user)s pki_self_signed_token=internal -# for supporting server cert SAN injection -pki_san_inject=False -pki_san_for_server_cert= pki_skip_configuration=False pki_skip_ds_verify=False pki_skip_installation=False pki_skip_sd_verify=False -pki_sslserver_key_algorithm=%(ipa_key_algorithm)s -pki_sslserver_key_size=%(ipa_key_size)s -pki_sslserver_key_type=%(ipa_key_type)s pki_sslserver_token=internal -# nickname and subject are hard-coded pki_sslserver_nickname=Server-Cert cert-pki-ca pki_sslserver_subject_dn=cn=%(ipa_fqdn)s,%(ipa_subject_base)s -pki_subsystem_key_algorithm=%(ipa_key_algorithm)s -pki_subsystem_key_size=%(ipa_key_size)s -pki_subsystem_key_type=%(ipa_key_type)s -pki_subsystem_token=%(pki_token_name)s # nickname and subject are hard-coded pki_subsystem_nickname=subsystemCert cert-pki-ca pki_subsystem_subject_dn=cn=CA Subsystem,%(ipa_subject_base)s pki_theme_enable=True pki_theme_server_dir=/usr/share/pki/common-ui +pki_audit_group=pkiaudit +pki_group=pkiuser pki_user=pkiuser pki_existing=False @@ -161,14 +107,9 @@ pki_ds_hostname=%(ipa_fqdn)s [CA] -pki_ca_signing_key_algorithm=%(ipa_ca_signing_algorithm)s -pki_ca_signing_key_size=%(ipa_ca_key_size)s -pki_ca_signing_key_type=%(ipa_ca_key_type)s pki_ca_signing_record_create=True pki_ca_signing_serial_number=1 -pki_ca_signing_signing_algorithm=%(ipa_ca_signing_algorithm)s pki_ca_signing_subject_dn=%(ipa_ca_subject)s -pki_ca_signing_token=%(pki_token_name)s pki_ca_signing_csr_path=/root/ipa.csr @@ -183,40 +124,23 @@ pki_ca_signing_csr_path=/root/ipa.csr # pki_subsystem_cert_path= pki_ca_starting_crl_number=0 + pki_external=False pki_external_step_two=False pki_req_ext_add=False -# MS subca request ext data -pki_req_ext_oid=1.3.6.1.4.1.311.20.2 -pki_req_ext_critical=False -pki_req_ext_data=1E0A00530075006200430041 pki_external_pkcs12_path=%(pki_pkcs12_path)s pki_external_pkcs12_password=%(pki_pkcs12_password)s pki_import_admin_cert=False -pki_ocsp_signing_key_algorithm=%(ipa_key_algorithm)s -pki_ocsp_signing_key_size=%(ipa_key_size)s -pki_ocsp_signing_key_type=%(ipa_key_type)s -pki_ocsp_signing_signing_algorithm=%(ipa_signing_algorithm)s -pki_ocsp_signing_token=%(pki_token_name)s -# nickname and subject are hard-coded pki_ocsp_signing_nickname=ocspSigningCert cert-pki-ca pki_ocsp_signing_subject_dn=cn=OCSP Subsystem,%(ipa_subject_base)s pki_profiles_in_ldap=True -pki_random_serial_numbers_enable=False pki_subordinate=False pki_subordinate_create_new_security_domain=False ### pki_subordinate_security_domain_name=%(pki_dns_domainname)s Subordinate Security Domain -pki_admin_email=%(ipa_admin_email)s -pki_admin_name=%(ipa_admin_user)s -pki_admin_nickname=%(ipa_admin_nickname)s -pki_admin_subject_dn=cn=%(ipa_admin_nickname)s,%(ipa_subject_base)s -pki_admin_uid=%(ipa_admin_user)s - -# nickname and subject are hard-coded pki_audit_signing_nickname=auditSigningCert cert-pki-ca pki_audit_signing_subject_dn=cn=CA Audit,%(ipa_subject_base)s @@ -236,7 +160,6 @@ pki_replica_number_range_end=100 [KRA] pki_import_admin_cert=True pki_standalone=False -pki_kra_ephemeral_requests=True pki_ds_create_new_db=False # pki_admin_csr_path= @@ -255,27 +178,11 @@ pki_external_step_two=False # pki_subsystem_cert_path= # pki_transport_cert_path= -pki_storage_key_algorithm=%(ipa_key_algorithm)s -pki_storage_key_size=%(ipa_key_size)s -pki_storage_key_type=%(ipa_key_type)s pki_storage_nickname=storageCert cert-pki-kra -pki_storage_signing_algorithm=SHA256withRSA pki_storage_subject_dn=cn=KRA Storage Certificate,%(ipa_subject_base)s -pki_storage_token=%(pki_token_name)s -pki_transport_key_algorithm=%(ipa_key_algorithm)s -pki_transport_key_size=%(ipa_key_size)s -pki_transport_key_type=%(ipa_key_type)s pki_transport_nickname=transportCert cert-pki-kra -pki_transport_signing_algorithm=SHA256withRSA pki_transport_subject_dn=cn=KRA Transport Certificate,%(ipa_subject_base)s -pki_transport_token=%(pki_token_name)s - -pki_admin_email=%(ipa_admin_email)s -pki_admin_name=%(ipa_admin_user)s -pki_admin_nickname=%(ipa_admin_nickname)s -pki_admin_subject_dn=cn=%(ipa_admin_nickname)s,%(ipa_subject_base)s -pki_admin_uid=%(ipa_admin_user)s pki_audit_signing_nickname=auditSigningCert cert-pki-kra pki_audit_signing_subject_dn=cn=KRA Audit,%(ipa_subject_base)s diff --git a/install/share/ipaca_softhsm2.ini b/install/share/ipaca_softhsm2.ini new file mode 100644 index 000000000..52d05825b --- /dev/null +++ b/install/share/ipaca_softhsm2.ini @@ -0,0 +1,9 @@ +# +# Example config for softhsm2 +# + +[DEFAULT] +pki_hsm_enable=True +pki_hsm_libfile=%(softhsm2_so)s +pki_hsm_modulename=softhsm2 +pki_token_name=softhsm_token diff --git a/install/tools/ipa-ca-install.in b/install/tools/ipa-ca-install.in index 1782f311b..5307adfef 100644 --- a/install/tools/ipa-ca-install.in +++ b/install/tools/ipa-ca-install.in @@ -86,6 +86,7 @@ def parse_options(): type="choice", choices=ca_algos, metavar="{{{0}}}".format(",".join(ca_algos)), help="Signing algorithm of the IPA CA certificate") + parser.add_option("-P", "--principal", dest="principal", sensitive=True, default=None, help="User allowed to manage replicas") parser.add_option("--subject-base", dest="subject_base", @@ -101,6 +102,10 @@ def parse_options(): "(default CN=Certificate Authority,O=). " "RDNs are in LDAP order (most specific RDN first).")) + parser.add_option("--pki-config-override", dest="pki_config_override", + default=None, + help="Path to ini file with config overrides.") + options, args = parser.parse_args() safe_options = parser.get_safe_opts(options) diff --git a/ipaserver/install/ca.py b/ipaserver/install/ca.py index 47730616e..1097e9b26 100644 --- a/ipaserver/install/ca.py +++ b/ipaserver/install/ca.py @@ -336,22 +336,25 @@ def install_step_0(standalone, replica_config, options, custodia): ca = cainstance.CAInstance( realm=realm_name, host_name=host_name, custodia=custodia ) - ca.configure_instance(host_name, dm_password, dm_password, - subject_base=subject_base, - ca_subject=ca_subject, - ca_signing_algorithm=ca_signing_algorithm, - ca_type=ca_type, - external_ca_profile=external_ca_profile, - csr_file=csr_file, - cert_file=cert_file, - cert_chain_file=cert_chain_file, - pkcs12_info=pkcs12_info, - master_host=master_host, - master_replication_port=master_replication_port, - ra_p12=ra_p12, - ra_only=ra_only, - promote=promote, - use_ldaps=use_ldaps) + ca.configure_instance( + host_name, dm_password, dm_password, + subject_base=subject_base, + ca_subject=ca_subject, + ca_signing_algorithm=ca_signing_algorithm, + ca_type=ca_type, + external_ca_profile=external_ca_profile, + csr_file=csr_file, + cert_file=cert_file, + cert_chain_file=cert_chain_file, + pkcs12_info=pkcs12_info, + master_host=master_host, + master_replication_port=master_replication_port, + ra_p12=ra_p12, + ra_only=ra_only, + promote=promote, + use_ldaps=use_ldaps, + pki_config_override=options.pki_config_override, + ) def install_step_1(standalone, replica_config, options, custodia): diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py index cec27e42c..5fe1c7ac7 100644 --- a/ipaserver/install/cainstance.py +++ b/ipaserver/install/cainstance.py @@ -306,6 +306,7 @@ class CAInstance(DogtagInstance): self.csr_file = None self.cert_file = None self.cert_chain_file = None + self.basedn = DN(('o', 'ipaca')) if realm is not None: self.canickname = get_ca_nickname(realm) @@ -327,7 +328,8 @@ class CAInstance(DogtagInstance): ca_signing_algorithm=None, ca_type=None, external_ca_profile=None, ra_p12=None, ra_only=False, - promote=False, use_ldaps=False): + promote=False, use_ldaps=False, + pki_config_override=None): """Create a CA instance. To create a clone, pass in pkcs12_info. @@ -370,6 +372,7 @@ class CAInstance(DogtagInstance): self.no_db_setup = promote self.use_ldaps = use_ldaps + self.pki_config_override = pki_config_override # Determine if we are installing as an externally-signed CA and # what stage we're in. @@ -569,6 +572,8 @@ class CAInstance(DogtagInstance): pki_external_step_two=True, ) + nolog_list = [self.dm_password, self.admin_password, pki_pin] + config = self._create_spawn_config(cfg) pent = pwd.getpwnam(self.service_user) with tempfile.NamedTemporaryFile('w') as f: @@ -580,7 +585,7 @@ class CAInstance(DogtagInstance): DogtagInstance.spawn_instance( self, f.name, - nolog_list=(self.dm_password, self.admin_password, pki_pin) + nolog_list=nolog_list ) if self.external == 1: diff --git a/ipaserver/install/dogtag.py b/ipaserver/install/dogtag.py index a5c2a5a70..7d1fe5496 100644 --- a/ipaserver/install/dogtag.py +++ b/ipaserver/install/dogtag.py @@ -5,6 +5,12 @@ """ Dogtag-based service installer module """ +import os + +# pylint: disable=import-error +from six.moves.configparser import RawConfigParser +# pylint: enable=import-error + from ipalib.install import service from ipalib.install.service import prepare_only, replica_install_only @@ -23,3 +29,24 @@ class DogtagInstallInterface(service.ServiceInstallInterface): ) ca_file = prepare_only(ca_file) ca_file = replica_install_only(ca_file) + + pki_config_override = knob( + str, None, + cli_names='--pki-config-override', + description="Path to ini file with config overrides.", + ) + + @pki_config_override.validator + def pki_config_override(self, value): + if value is None: + return + if not os.path.isabs(value): + raise ValueError("must use an absolute path") + try: + cfg = RawConfigParser() + with open(value) as f: + cfg.readfp(f) # pylint: disable=deprecated-method + except Exception as e: + raise ValueError( + "Invalid config '{}': {}".format(value, e) + ) diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py index 60b422513..a565b9bf8 100644 --- a/ipaserver/install/dogtaginstance.py +++ b/ipaserver/install/dogtaginstance.py @@ -29,6 +29,8 @@ import shutil import traceback import dbus +from configparser import DEFAULTSECT, ConfigParser, RawConfigParser + import six from pki.client import PKIConnection @@ -49,13 +51,7 @@ from ipaserver.install import sysupgrade from ipaserver.install import replication from ipaserver.install.installutils import stopped_service -# pylint: disable=import-error -if six.PY3: - from configparser import DEFAULTSECT, ConfigParser, RawConfigParser -else: - from ConfigParser import DEFAULTSECT, RawConfigParser - from ConfigParser import SafeConfigParser as ConfigParser -# pylint: enable=import-error + logger = logging.getLogger(__name__) @@ -112,10 +108,6 @@ class DogtagInstance(service.Service): b'userdn="ldap:///uid=*,ou=people,o=ipaca";)' ) - ipaca_default = os.path.join( - paths.USR_SHARE_IPA_DIR, "ipaca_default.ini" - ) - def __init__(self, realm, subsystem, service_desc, host_name=None, nss_db=paths.PKI_TOMCAT_ALIAS_DIR, service_prefix=None, config=None): @@ -134,7 +126,7 @@ class DogtagInstance(service.Service): self.pkcs12_info = None self.clone = False - self.basedn = DN(('o', 'ipaca')) + self.basedn = None self.admin_user = "admin" self.admin_dn = DN( ('uid', self.admin_user), self.ipaca_people @@ -142,7 +134,6 @@ class DogtagInstance(service.Service): self.admin_groups = None self.tmp_agent_db = None self.subsystem = subsystem - self.security_domain_name = "IPA" # replication parameters self.master_host = None self.master_replication_port = 389 @@ -150,9 +141,10 @@ class DogtagInstance(service.Service): self.nss_db = nss_db self.config = config # Path to CS.cfg + # filled out by configure_instance + self.pki_config_override = None self.ca_subject = None self.subject_base = None - # filled out by configure_instance def is_installed(self): """ @@ -213,7 +205,6 @@ class DogtagInstance(service.Service): """ Enable client auth connection to the internal db. """ - with stopped_service('pki-tomcatd', 'pki-tomcat'): directivesetter.set_directive( self.config, @@ -613,66 +604,160 @@ class DogtagInstance(service.Service): ) def _create_spawn_config(self, subsystem_config): - """Create config instance - """ - defaults = dict( + loader = PKIIniLoader( + subsystem=self.subsystem, + fqdn=self.fqdn, + domain=api.env.domain, + basedn=self.basedn, # o=ipaca / o=kra,o=ipaca + subject_base=self.subject_base, + ca_subject=self.ca_subject, + admin_user=self.admin_user, + admin_password=self.admin_password, + dm_password=self.dm_password, + pki_config_override=self.pki_config_override + ) + return loader.create_spawn_config(subsystem_config) + + +class PKIIniLoader: + ipaca_default = os.path.join( + paths.USR_SHARE_IPA_DIR, 'ipaca_default.ini' + ) + ipaca_customize = os.path.join( + paths.USR_SHARE_IPA_DIR, 'ipaca_customize.ini' + ) + + security_domain_name = 'IPA' + ipaca_database = 'ipaca' + admin_nickname = 'ipa-ca-agent' + token_stanzas = [ + 'pki_audit_signing_token', + 'pki_subsystem_token', + 'pki_ca_signing_token', + 'pki_ocsp_signing_token', + 'pki_storage_token', + 'pki_transport_token', + ] + + def __init__(self, subsystem, fqdn, domain, basedn, + subject_base, ca_subject, admin_user, admin_password, + dm_password, pki_config_override=None): + self.pki_config_override = pki_config_override + self.defaults = dict( # pretty much static ipa_security_domain_name=self.security_domain_name, - ipa_ds_database=u"ipaca", - ipa_ds_base_dn=self.basedn, - ipa_admin_user=self.admin_user, - ipa_admin_nickname="ipa-ca-agent", + ipa_ds_database=self.ipaca_database, + ipa_ds_base_dn=basedn, + ipa_admin_nickname=self.admin_nickname, ipa_ca_pem_file=paths.IPA_CA_CRT, # variable - ipa_ca_subject=self.ca_subject, - ipa_subject_base=self.subject_base, - ipa_fqdn=self.fqdn, + ipa_ca_subject=ca_subject, + ipa_subject_base=subject_base, + ipa_fqdn=fqdn, ipa_ocsp_uri="http://{}.{}/ca/ocsp".format( - IPA_CA_RECORD, ipautil.format_netloc(api.env.domain)), + IPA_CA_RECORD, ipautil.format_netloc(domain)), ipa_admin_cert_p12=paths.DOGTAG_ADMIN_P12, - pki_admin_password=self.admin_password, - pki_ds_password=self.dm_password, + ipa_admin_user=admin_user, + pki_admin_password=admin_password, + pki_ds_password=dm_password, # Dogtag's pkiparser defines these config vars by default: - pki_dns_domainname=api.env.domain, - pki_hostname=self.fqdn, - pki_subsystem=self.subsystem.upper(), - pki_subsystem_type=self.subsystem.lower(), + pki_dns_domainname=domain, + pki_hostname=fqdn, + pki_subsystem=subsystem.upper(), + pki_subsystem_type=subsystem.lower(), home_dir=os.path.expanduser("~"), + # for softhsm2 testing + softhsm2_so=paths.LIBSOFTHSM2_SO ) - def mangle_values(d): - """Stringify and quote % as %% to avoid interpolation errors + def _mangle_values(self, dct): + """Stringify and quote % as %% to avoid interpolation errors - * booleans are converted to 'True', 'False' - * DN and numbers are converted to string - * None is turned into empty string '' - """ - result = {} - for k, v in d.items(): - if isinstance(v, (DN, bool, six.integer_types)): - v = six.text_type(v) - elif v is None: - v = '' - result[k] = v.replace('%', '%%') - return result + * booleans are converted to 'True', 'False' + * DN and numbers are converted to string + * None is turned into empty string '' + """ + result = {} + for k, v in dct.items(): + if isinstance(v, (DN, bool, six.integer_types)): + v = six.text_type(v) + elif v is None: + v = '' + result[k] = v.replace('%', '%%') + return result - defaults = mangle_values(defaults) - subsystem_config = mangle_values(subsystem_config) + def _verify_immutable(self, config, immutable_settings, filename): + section_name = self.defaults['pki_subsystem'] + errors = [] + for key, isvalue in sorted(immutable_settings.items()): + cfgvalue = config.get(section_name, key) + if isvalue != cfgvalue: + errors.append(f"{key}: '{cfgvalue}' != '{isvalue}'") + if errors: + raise ValueError( + '{} overrides immutable options:\n{}'.format( + filename, '\n'.join(errors) + ) + ) + + def create_spawn_config(self, subsystem_config): + """Create config instance + """ + section_name = self.defaults['pki_subsystem'] + defaults = self._mangle_values(self.defaults) # create a config template with interpolation support # read base config cfgtpl = ConfigParser(defaults=defaults) cfgtpl.optionxform = str with open(self.ipaca_default) as f: - cfgtpl.readfp(f) # pylint: disable=deprecated-method + cfgtpl.read_file(f) + # overwrite defaults with our defaults for key, value in defaults.items(): cfgtpl.set(DEFAULTSECT, key, value) + # overwrite CA/KRA config with subsystem settings - section_name = self.subsystem.upper() + subsystem_config = self._mangle_values(subsystem_config) for key, value in subsystem_config.items(): cfgtpl.set(section_name, key, value) + # get a list of settings that cannot be modified by users + immutable_settings = { + k: v for k, v in cfgtpl.items(section=section_name) + if k.startswith('pki_') + } + + # add ipaca_customize overlay, + # These are settings that can be modified by a user, too. We use + # ipaca_customize.ini to set sensible defaults. + with open(self.ipaca_customize) as f: + cfgtpl.read_file(f) + + # sanity check + self._verify_immutable( + cfgtpl, immutable_settings, self.ipaca_customize + ) + + # load external overlay from command line + if self.pki_config_override is not None: + with open(self.pki_config_override) as f: + cfgtpl.read_file(f) + self._verify_immutable( + cfgtpl, immutable_settings, self.pki_config_override + ) + + # key backup is not compatible with HSM support + if (cfgtpl.has_option(section_name, 'pki_hsm_enable') and + cfgtpl.getboolean(section_name, 'pki_hsm_enable')): + cfgtpl.set(section_name, 'pki_backup_keys', 'False') + cfgtpl.set(section_name, 'pki_backup_password', '') + + pki_token_name = cfgtpl.get(section_name, 'pki_token_name') + for stanza in self.token_stanzas: + if cfgtpl.has_option(section_name, stanza): + cfgtpl.set(section_name, stanza, pki_token_name) + # Next up, get rid of interpolation variables, DEFAULT, # irrelevant sections and unused variables. Only the subsystem # section is copied into a new raw config parser. A raw config @@ -686,3 +771,30 @@ class DogtagInstance(service.Service): config.set(section_name, key, value) return config + + +def test(): + import sys + + loader = PKIIniLoader( + subsystem='CA', + fqdn='replica.ipa.example', + domain='ipa.example', + basedn='o=ipaca', + subject_base='o=IPA,o=EXAMPLE', + ca_subject='cn=CA,o=IPA,o=EXAMPLE', + admin_user='admin', + admin_password='Secret1', + dm_password='Secret2', + pki_config_override='install/share/ipaca_softhsm2.ini', + ) + loader.ipaca_default = 'install/share/ipaca_default.ini' + loader.ipaca_customize = 'install/share/ipaca_customize.ini' + config = loader.create_spawn_config(dict( + pki_external=True + )) + config.write(sys.stdout, False) + + +if __name__ == '__main__': + test() diff --git a/ipaserver/install/ipa_kra_install.py b/ipaserver/install/ipa_kra_install.py index f70fd5216..2d6e4bf09 100644 --- a/ipaserver/install/ipa_kra_install.py +++ b/ipaserver/install/ipa_kra_install.py @@ -75,6 +75,11 @@ class KRAInstall(admintool.AdminTool): dest="uninstall", action="store_true", default=False, help=SUPPRESS_HELP) + parser.add_option( + "--pki-config-override", dest="pki_config_override", + default=None, + help="Path to ini file with config overrides.") + def validate_options(self, needs_root=True): super(KRAInstall, self).validate_options(needs_root=True) diff --git a/ipaserver/install/kra.py b/ipaserver/install/kra.py index ab1e3f639..3cc020896 100644 --- a/ipaserver/install/kra.py +++ b/ipaserver/install/kra.py @@ -87,11 +87,14 @@ def install(api, replica_config, options, custodia): promote = True kra = krainstance.KRAInstance(realm_name) - kra.configure_instance(realm_name, host_name, dm_password, dm_password, - subject_base=subject_base, - pkcs12_info=pkcs12_info, - master_host=master_host, - promote=promote) + kra.configure_instance( + realm_name, host_name, dm_password, dm_password, + subject_base=subject_base, + pkcs12_info=pkcs12_info, + master_host=master_host, + promote=promote, + pki_config_override=options.pki_config_override, + ) _service.print_msg("Restarting the directory server") ds = dsinstance.DsInstance() diff --git a/ipaserver/install/krainstance.py b/ipaserver/install/krainstance.py index f53d171ec..24a2e85cb 100644 --- a/ipaserver/install/krainstance.py +++ b/ipaserver/install/krainstance.py @@ -77,7 +77,7 @@ class KRAInstance(DogtagInstance): def configure_instance(self, realm_name, host_name, dm_password, admin_password, pkcs12_info=None, master_host=None, subject_base=None, subject=None, - promote=False): + promote=False, pki_config_override=None): """Create a KRA instance. To create a clone, pass in pkcs12_info. @@ -90,6 +90,7 @@ class KRAInstance(DogtagInstance): if self.pkcs12_info is not None or promote: self.clone = True self.master_host = master_host + self.pki_config_override = pki_config_override self.subject_base = \ subject_base or installutils.default_subject_base(realm_name) @@ -213,13 +214,14 @@ class KRAInstance(DogtagInstance): with open(cfg_file, "w") as f: config.write(f) + nolog_list = [ + self.dm_password, self.admin_password, pki_pin, tmp_agent_pwd + ] + try: DogtagInstance.spawn_instance( self, cfg_file, - nolog_list=(self.dm_password, - self.admin_password, - pki_pin, - tmp_agent_pwd) + nolog_list=nolog_list ) finally: os.remove(p12_tmpfile_name) diff --git a/ipatests/pytest_ipa/integration/tasks.py b/ipatests/pytest_ipa/integration/tasks.py index 442b04f11..f4a0c95be 100644 --- a/ipatests/pytest_ipa/integration/tasks.py +++ b/ipatests/pytest_ipa/integration/tasks.py @@ -39,7 +39,7 @@ from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.backends import default_backend - +from ipapython import certdb from ipapython import ipautil from ipaplatform.paths import paths from ipapython.dn import DN @@ -1496,6 +1496,32 @@ def run_certutil(host, args, reqdir, dbtype=None, stdin_text=stdin) +def certutil_certs_keys(host, reqdir, pwd_file, token_name=None): + """Run certutils and get mappings of cert and key files + """ + base_args = ['-f', pwd_file] + if token_name is not None: + base_args.extend(['-h', token_name]) + cert_args = base_args + ['-L'] + key_args = base_args + ['-K'] + + result = run_certutil(host, cert_args, reqdir) + certs = {} + for line in result.stdout_text.splitlines(): + mo = certdb.CERT_RE.match(line) + if mo: + certs[mo.group('nick')] = mo.group('flags') + + result = run_certutil(host, key_args, reqdir) + assert 'orphan' not in result.stdout_text + keys = {} + for line in result.stdout_text.splitlines(): + mo = certdb.KEY_RE.match(line) + if mo: + keys[mo.group('nick')] = mo.group('keyid') + return certs, keys + + def upload_temp_contents(host, contents, encoding='utf-8'): """Upload contents to a temporary file