diff --git a/install/share/Makefile.am b/install/share/Makefile.am index ae09afdc4..b93cb0cec 100644 --- a/install/share/Makefile.am +++ b/install/share/Makefile.am @@ -48,6 +48,7 @@ dist_app_DATA = \ bind.ipa-ext.conf.template \ bind.ipa-options-ext.conf.template \ bind.named.conf.template \ + bind.openssl.cnf.template \ certmap.conf.template \ kdc.conf.template \ kdc_extensions.template \ diff --git a/install/share/bind.openssl.cnf.template b/install/share/bind.openssl.cnf.template new file mode 100644 index 000000000..b43b46fef --- /dev/null +++ b/install/share/bind.openssl.cnf.template @@ -0,0 +1,14 @@ +# OpenSSL configuration file +# File generated by IPA instalation +openssl_conf = openssl_init + +[openssl_init] +engines = engine_section + +[engine_section] +$OPENSSL_ENGINE = ${OPENSSL_ENGINE}_section + +[${OPENSSL_ENGINE}_section] +engine_id = $OPENSSL_ENGINE +MODULE_PATH = $SOFTHSM_MODULE +init=0 diff --git a/ipaplatform/base/constants.py b/ipaplatform/base/constants.py index eac60cac3..08b34708a 100644 --- a/ipaplatform/base/constants.py +++ b/ipaplatform/base/constants.py @@ -23,6 +23,8 @@ class BaseConstantsNamespace: NAMED_USER = "named" NAMED_GROUP = "named" NAMED_DATA_DIR = "data/" + NAMED_OPTIONS_VAR = "OPTIONS" + NAMED_OPENSSL_ENGINE = None NAMED_ZONE_COMMENT = "" PKI_USER = 'pkiuser' PKI_GROUP = 'pkiuser' diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py index e1c7199b1..5e1f41f38 100644 --- a/ipaplatform/base/paths.py +++ b/ipaplatform/base/paths.py @@ -68,6 +68,7 @@ class BasePathNamespace: IPA_DEFAULT_CONF = "/etc/ipa/default.conf" IPA_DNSKEYSYNCD_KEYTAB = "/etc/ipa/dnssec/ipa-dnskeysyncd.keytab" IPA_ODS_EXPORTER_KEYTAB = "/etc/ipa/dnssec/ipa-ods-exporter.keytab" + DNSSEC_OPENSSL_CONF = None DNSSEC_SOFTHSM2_CONF = "/etc/ipa/dnssec/softhsm2.conf" DNSSEC_SOFTHSM_PIN_SO = "/etc/ipa/dnssec/softhsm_pin_so" IPA_NSSDB_DIR = "/etc/ipa/nssdb" diff --git a/ipaplatform/redhat/services.py b/ipaplatform/redhat/services.py index 042431849..3cc8a71b8 100644 --- a/ipaplatform/redhat/services.py +++ b/ipaplatform/redhat/services.py @@ -68,6 +68,7 @@ redhat_system_units['ipa-dnskeysyncd'] = 'ipa-dnskeysyncd.service' redhat_system_units['named-regular'] = 'named.service' redhat_system_units['named-pkcs11'] = 'named-pkcs11.service' redhat_system_units['named'] = redhat_system_units['named-pkcs11'] +redhat_system_units['named-conflict'] = redhat_system_units['named-regular'] redhat_system_units['ods-enforcerd'] = 'ods-enforcerd.service' redhat_system_units['ods_enforcerd'] = redhat_system_units['ods-enforcerd'] redhat_system_units['ods-signerd'] = 'ods-signerd.service' diff --git a/ipaserver/dnssec/bindmgr.py b/ipaserver/dnssec/bindmgr.py index c2f9c5a04..4f7cad893 100644 --- a/ipaserver/dnssec/bindmgr.py +++ b/ipaserver/dnssec/bindmgr.py @@ -16,11 +16,14 @@ import stat import six import ipalib.constants + from ipapython.dn import DN from ipapython import ipautil +from ipaplatform.constants import constants as platformconstants from ipaplatform.paths import paths from ipaserver.dnssec.temp import TemporaryDirectory +from ipaserver.install import installutils logger = logging.getLogger(__name__) @@ -133,8 +136,11 @@ class BINDMgr: cmd.extend(['-f', 'KSK']) if attrs.get('idnsSecKeyRevoke', [b'FALSE'])[0].upper() == b'TRUE': cmd.extend(['-R', datetime.now().strftime(time_bindfmt)]) + if platformconstants.NAMED_OPENSSL_ENGINE is not None: + cmd.extend(['-E', platformconstants.NAMED_OPENSSL_ENGINE]) cmd.append(zone.to_text()) + installutils.check_entropy() # keys has to be readable by ODS & named result = ipautil.run(cmd, capture_output=True) basename = result.output.strip() diff --git a/ipaserver/install/bindinstance.py b/ipaserver/install/bindinstance.py index 90cc9b38b..b27548144 100644 --- a/ipaserver/install/bindinstance.py +++ b/ipaserver/install/bindinstance.py @@ -657,7 +657,7 @@ class BindInstance(service.Service): self.no_dnssec_validation = False self.sub_dict = None self.reverse_zones = () - self.named_regular = services.service('named-regular', api) + self.named_conflict = services.service('named-conflict', api) suffix = ipautil.dn_attribute_property('_suffix') @@ -764,7 +764,7 @@ class BindInstance(service.Service): # named has to be started after softhsm initialization # self.step("restarting named", self.__start) - self.step("configuring named to start on boot", self.__enable) + self.step("configuring named to start on boot", self.switch_service) self.step("changing resolv.conf to point to ourselves", self.__setup_resolv_conf) self.start_creation() @@ -774,19 +774,16 @@ class BindInstance(service.Service): def __start(self): try: - if self.get_state("running") is None: - # first time store status - self.backup_state("running", self.is_running()) self.restart() except Exception as e: logger.error("Named service failed to start (%s)", e) print("named service failed to start") + def switch_service(self): + self.mask_conflict() + self.__enable() + def __enable(self): - if self.get_state("enabled") is None: - self.backup_state("enabled", self.is_running()) - self.backup_state("named-regular-enabled", - self.named_regular.is_running()) # We do not let the system start IPA components on its own, # Instead we reply on the IPA init script to start only enabled # components as found in our LDAP configuration tree @@ -797,20 +794,19 @@ class BindInstance(service.Service): # don't crash, just report error logger.error("DNS service already exists") - # disable named, we need to run named-pkcs11 only - if self.get_state("named-regular-running") is None: - # first time store status - self.backup_state("named-regular-running", - self.named_regular.is_running()) + def mask_conflict(self): + # disable named-conflict (either named or named-pkcs11) try: - self.named_regular.stop() + self.named_conflict.stop() except Exception as e: - logger.debug("Unable to stop named (%s)", e) + logger.debug("Unable to stop %s (%s)", + self.named_conflict.systemd_name, e) try: - self.named_regular.mask() + self.named_conflict.mask() except Exception as e: - logger.debug("Unable to mask named (%s)", e) + logger.debug("Unable to mask %s (%s)", + self.named_conflict.systemd_name, e) def _get_dnssec_validation(self): """get dnssec-validation value @@ -1307,11 +1303,6 @@ class BindInstance(service.Service): if self.is_configured(): self.print_msg("Unconfiguring %s" % self.service_name) - running = self.restore_state("running") - enabled = self.restore_state("enabled") - named_regular_running = self.restore_state("named-regular-running") - named_regular_enabled = self.restore_state("named-regular-enabled") - self.dns_backup.clear_records(self.api.Backend.ldap2.isconnected()) try: @@ -1326,23 +1317,10 @@ class BindInstance(service.Service): ipautil.rmtree(paths.BIND_LDAP_DNS_IPA_WORKDIR) - # disabled by default, by ldap_configure() - if enabled: - self.enable() - else: - self.disable() + self.disable() + self.stop() - if running: - self.restart() - else: - self.stop() - - self.named_regular.unmask() - if named_regular_enabled: - self.named_regular.enable() - - if named_regular_running: - self.named_regular.start() + self.named_conflict.unmask() ipautil.remove_file(paths.NAMED_CONF_BAK) ipautil.remove_file(paths.NAMED_CUSTOM_CONF) diff --git a/ipaserver/install/dnskeysyncinstance.py b/ipaserver/install/dnskeysyncinstance.py index 0cc5cd0c4..d3637cf79 100644 --- a/ipaserver/install/dnskeysyncinstance.py +++ b/ipaserver/install/dnskeysyncinstance.py @@ -164,6 +164,61 @@ class DNSKeySyncInstance(service.Service): self._ldap_mod("dnssec.ldif", {'SUFFIX': self.suffix, }) + def setup_named_openssl_conf(self): + if constants.NAMED_OPENSSL_ENGINE is not None: + logger.debug("Setup OpenSSL config for BIND") + # setup OpenSSL config for BIND, + # this one is needed because FreeIPA installation + # disables p11-kit-proxy PKCS11 module + conf_file_dict = { + 'OPENSSL_ENGINE': constants.NAMED_OPENSSL_ENGINE, + 'SOFTHSM_MODULE': paths.LIBSOFTHSM2_SO, + } + named_openssl_txt = ipautil.template_file( + os.path.join(paths.USR_SHARE_IPA_DIR, + "bind.openssl.cnf.template"), + conf_file_dict) + with open(paths.DNSSEC_OPENSSL_CONF, 'w') as f: + os.fchmod(f.fileno(), 0o640) + os.fchown(f.fileno(), 0, self.named_gid) + f.write(named_openssl_txt) + + def setup_named_sysconfig(self): + logger.debug("Setup BIND sysconfig") + sysconfig = paths.SYSCONFIG_NAMED + self.fstore.backup_file(sysconfig) + + directivesetter.set_directive( + sysconfig, + 'SOFTHSM2_CONF', paths.DNSSEC_SOFTHSM2_CONF, + quotes=False, separator='=') + + if constants.NAMED_OPENSSL_ENGINE is not None: + directivesetter.set_directive( + sysconfig, + 'OPENSSL_CONF', paths.DNSSEC_OPENSSL_CONF, + quotes=False, separator='=') + + engine_txt = "-E {}".format(constants.NAMED_OPENSSL_ENGINE) + directivesetter.set_directive( + sysconfig, + constants.NAMED_OPTIONS_VAR, engine_txt, + quotes=True, separator='=') + + def setup_ipa_dnskeysyncd_sysconfig(self): + logger.debug("Setup ipa-dnskeysyncd sysconfig") + sysconfig = paths.SYSCONFIG_IPA_DNSKEYSYNCD + directivesetter.set_directive( + sysconfig, + 'SOFTHSM2_CONF', paths.DNSSEC_SOFTHSM2_CONF, + quotes=False, separator='=') + + if constants.NAMED_OPENSSL_ENGINE is not None: + directivesetter.set_directive( + sysconfig, + 'OPENSSL_CONF', paths.DNSSEC_OPENSSL_CONF, + quotes=False, separator='=') + def __setup_softhsm(self): assert self.ods_uid is not None assert self.named_gid is not None @@ -186,23 +241,15 @@ class DNSKeySyncInstance(service.Service): 'tokens_dir': paths.DNSSEC_TOKENS_DIR } logger.debug("Creating new softhsm config file") - named_fd = open(paths.DNSSEC_SOFTHSM2_CONF, 'w') - named_fd.seek(0) - named_fd.truncate(0) - named_fd.write(softhsm_conf_txt) - named_fd.close() - os.chmod(paths.DNSSEC_SOFTHSM2_CONF, 0o644) + with open(paths.DNSSEC_SOFTHSM2_CONF, 'w') as f: + os.fchmod(f.fileno(), 0o644) + f.write(softhsm_conf_txt) - # setting up named to use softhsm2 - if not self.fstore.has_file(paths.SYSCONFIG_NAMED): - self.fstore.backup_file(paths.SYSCONFIG_NAMED) - - # setting up named and ipa-dnskeysyncd to use our softhsm2 config - for sysconfig in [paths.SYSCONFIG_NAMED, - paths.SYSCONFIG_IPA_DNSKEYSYNCD]: - directivesetter.set_directive(sysconfig, 'SOFTHSM2_CONF', - paths.DNSSEC_SOFTHSM2_CONF, - quotes=False, separator='=') + # setting up named and ipa-dnskeysyncd to use our softhsm2 and + # openssl configs + self.setup_named_openssl_conf() + self.setup_named_sysconfig() + self.setup_ipa_dnskeysyncd_sysconfig() if (token_dir_exists and os.path.exists(paths.DNSSEC_SOFTHSM_PIN) and os.path.exists(paths.DNSSEC_SOFTHSM_PIN_SO)): @@ -231,23 +278,17 @@ class DNSKeySyncInstance(service.Service): entropy_bits=0, special=None, min_len=pin_length) logger.debug("Saving user PIN to %s", paths.DNSSEC_SOFTHSM_PIN) - named_fd = open(paths.DNSSEC_SOFTHSM_PIN, 'w') - named_fd.seek(0) - named_fd.truncate(0) - named_fd.write(pin) - named_fd.close() - os.chmod(paths.DNSSEC_SOFTHSM_PIN, 0o770) - # chown to ods:named - os.chown(paths.DNSSEC_SOFTHSM_PIN, self.ods_uid, self.named_gid) + with open(paths.DNSSEC_SOFTHSM_PIN, 'w') as f: + # chown to ods:named + os.fchown(f.fileno(), self.ods_uid, self.named_gid) + os.fchmod(f.fileno(), 0o660) + f.write(pin) logger.debug("Saving SO PIN to %s", paths.DNSSEC_SOFTHSM_PIN_SO) - named_fd = open(paths.DNSSEC_SOFTHSM_PIN_SO, 'w') - named_fd.seek(0) - named_fd.truncate(0) - named_fd.write(pin_so) - named_fd.close() - # owner must be root - os.chmod(paths.DNSSEC_SOFTHSM_PIN_SO, 0o400) + with open(paths.DNSSEC_SOFTHSM_PIN_SO, 'w') as f: + # owner must be root + os.fchmod(f.fileno(), 0o400) + f.write(pin_so) # initialize SoftHSM @@ -377,7 +418,7 @@ class DNSKeySyncInstance(service.Service): os.chown(dir_path, self.ods_uid, self.named_gid) for filename in files: file_path = os.path.join(root, filename) - os.chmod(file_path, 0o770 | stat.S_ISGID) + os.chmod(file_path, 0o660 | stat.S_ISGID) # chown to ods:named os.chown(file_path, self.ods_uid, self.named_gid) diff --git a/ipaserver/install/ipa_backup.py b/ipaserver/install/ipa_backup.py index d4b7b4377..64806db4c 100644 --- a/ipaserver/install/ipa_backup.py +++ b/ipaserver/install/ipa_backup.py @@ -185,6 +185,7 @@ class Backup(admintool.AdminTool): paths.OPENDNSSEC_KASP_FILE, paths.OPENDNSSEC_ZONELIST_FILE, paths.OPENDNSSEC_KASP_DB, + paths.DNSSEC_OPENSSL_CONF, paths.DNSSEC_SOFTHSM2_CONF, paths.DNSSEC_SOFTHSM_PIN_SO, paths.IPA_ODS_EXPORTER_KEYTAB, diff --git a/ipaserver/install/kra.py b/ipaserver/install/kra.py index 746c534dc..c7a097b58 100644 --- a/ipaserver/install/kra.py +++ b/ipaserver/install/kra.py @@ -106,9 +106,9 @@ def install(api, replica_config, options, custodia): # Restart apache for new proxy config file services.knownservices.httpd.restart(capture_output=True) - # Restarted named-pkcs11 to restore bind-dyndb-ldap operation, see + # Restarted named to restore bind-dyndb-ldap operation, see # https://pagure.io/freeipa/issue/5813 - named = services.knownservices.named # alias for named-pkcs11 + named = services.knownservices.named # alias for current named if named.is_running(): named.restart(capture_output=True) diff --git a/ipaserver/install/opendnssecinstance.py b/ipaserver/install/opendnssecinstance.py index 95029fd5e..044db794b 100644 --- a/ipaserver/install/opendnssecinstance.py +++ b/ipaserver/install/opendnssecinstance.py @@ -269,7 +269,7 @@ class OpenDNSSECInstance(service.Service): os.chown(dir_path, self.ods_uid, self.named_gid) # chown to ods:named for filename in files: file_path = os.path.join(root, filename) - os.chmod(file_path, 0o770 | stat.S_ISGID) + os.chmod(file_path, 0o660 | stat.S_ISGID) os.chown(file_path, self.ods_uid, self.named_gid) # chown to ods:named finally: diff --git a/ipaserver/install/service.py b/ipaserver/install/service.py index 3706bdd86..73373a15b 100644 --- a/ipaserver/install/service.py +++ b/ipaserver/install/service.py @@ -660,6 +660,7 @@ class Service: ] extra_config_opts.extend(config) + self.unmask() self.disable() set_service_entry_config(