diff --git a/debian/changelog b/debian/changelog index 120a1dcd7..96795d45c 100644 --- a/debian/changelog +++ b/debian/changelog @@ -4,6 +4,8 @@ freeipa (4.8.9-1) UNRELEASED; urgency=medium * control: Build freeipa-client-epn only where nodejs is available. (Closes: #970230) * install: ipa-print-pac belongs to the server instead of -client-epn. + * control, pkcs11-openssl-for-bind.diff: Add support for bind 9.16. + (LP: #1874568) -- Timo Aaltonen Mon, 14 Sep 2020 14:36:40 +0300 diff --git a/debian/control b/debian/control index 9480912db..4f63845fb 100644 --- a/debian/control +++ b/debian/control @@ -255,7 +255,6 @@ Depends: python3-ldap (>= 2.4.22), python3-systemd, slapi-nis (>= 0.56.1), - softhsm2 (>= 2.1.0-2), ssl-cert, sssd-dbus, systemd-sysv, @@ -278,9 +277,11 @@ Breaks: freeipa-server (<< 4.3.0-1) Replaces: freeipa-server (<< 4.3.0-1) Depends: freeipa-server (>= ${source:Version}), - bind9 (>= 1:9.11.3), - bind9-dyndb-ldap (>= 11.1-3), + bind9 (>= 1:9.16), + bind9-dyndb-ldap (>= 11.5-1~), + libengine-pkcs11-openssl, opendnssec (>= 1:2.1.5), + softhsm2, ${misc:Depends}, ${python3:Depends}, ${shlibs:Depends} diff --git a/debian/control.server b/debian/control.server index 9f1b6f7f7..d57b91712 100644 --- a/debian/control.server +++ b/debian/control.server @@ -40,7 +40,6 @@ Depends: python3-ldap (>= 2.4.22), python3-systemd, slapi-nis (>= 0.56.1), - softhsm2 (>= 2.1.0-2), ssl-cert, sssd-dbus, systemd-sysv, @@ -63,9 +62,11 @@ Breaks: freeipa-server (<< 4.3.0-1) Replaces: freeipa-server (<< 4.3.0-1) Depends: freeipa-server (>= ${source:Version}), - bind9 (>= 1:9.11.3), - bind9-dyndb-ldap (>= 11.1-3), + bind9 (>= 1:9.16), + bind9-dyndb-ldap (>= 11.5-1~), + libengine-pkcs11-openssl, opendnssec (>= 1:2.1.5), + softhsm2, ${misc:Depends}, ${python3:Depends}, ${shlibs:Depends} diff --git a/debian/patches/pkcs11-openssl-for-bind.diff b/debian/patches/pkcs11-openssl-for-bind.diff new file mode 100644 index 000000000..3b75b566c --- /dev/null +++ b/debian/patches/pkcs11-openssl-for-bind.diff @@ -0,0 +1,844 @@ +diff --git a/daemons/dnssec/ipa-dnskeysync-replica.in b/daemons/dnssec/ipa-dnskeysync-replica.in +index 6783e30ea..c5364a497 100644 +--- a/daemons/dnssec/ipa-dnskeysync-replica.in ++++ b/daemons/dnssec/ipa-dnskeysync-replica.in +@@ -145,7 +145,7 @@ def ldap2replica_zone_keys_sync(ldapkeydb, localhsm): + + + # IPA framework initialization +-standard_logging_setup(verbose=True, debug=True) ++standard_logging_setup(debug=True) + ipalib.api.bootstrap(context='dns', confdir=paths.ETC_IPA, in_server=True) + ipalib.api.finalize() + +diff --git a/daemons/dnssec/ipa-dnskeysyncd.in b/daemons/dnssec/ipa-dnskeysyncd.in +index 418bf73e2..13e6ac7f2 100644 +--- a/daemons/dnssec/ipa-dnskeysyncd.in ++++ b/daemons/dnssec/ipa-dnskeysyncd.in +@@ -23,12 +23,9 @@ logger = logging.getLogger(os.path.basename(__file__)) + + + # IPA framework initialization +-standard_logging_setup(verbose=True) ++standard_logging_setup(debug=True) + api.bootstrap(context='dns', confdir=paths.ETC_IPA, in_server=True) + api.finalize() +-if api.env.debug: +- root_logger = logging.getLogger() +- root_logger.setLevel(logging.DEBUG) + + # Global state + watcher_running = True +diff --git a/daemons/dnssec/ipa-ods-exporter.in b/daemons/dnssec/ipa-ods-exporter.in +index dd8606221..0349b9224 100644 +--- a/daemons/dnssec/ipa-ods-exporter.in ++++ b/daemons/dnssec/ipa-ods-exporter.in +@@ -29,12 +29,12 @@ import dns.dnssec + from gssapi.exceptions import GSSError + import six + import systemd.daemon +-import systemd.journal + + import ipalib + from ipalib.constants import SOFTHSM_DNSSEC_TOKEN_LABEL + from ipalib.install.kinit import kinit_keytab + from ipapython.dn import DN ++from ipapython.ipa_log_manager import standard_logging_setup + from ipapython import ipaldap + from ipaplatform.paths import paths + from ipaserver.dnssec.abshsm import sync_pkcs11_metadata, wrappingmech_name2id +@@ -650,20 +650,8 @@ def cleanup_ldap_zone(ldap, dns_dn, zone_name): + ldap.delete_entry(ldap_key) + + +-# this service is usually socket-activated +-root_logger = logging.getLogger() +-root_logger.addHandler(systemd.journal.JournalHandler()) +-root_logger.setLevel(level=logging.DEBUG) +- +-if len(sys.argv) > 2: +- print(__doc__) +- sys.exit(1) +-# program was likely invoked from console, log to it +-elif len(sys.argv) == 2: +- console = logging.StreamHandler() +- root_logger.addHandler(console) +- + # IPA framework initialization ++standard_logging_setup(debug=True) + ipalib.api.bootstrap(context='dns', confdir=paths.ETC_IPA, in_server=True) + ipalib.api.finalize() + +diff --git a/freeipa.spec.in b/freeipa.spec.in +index b4e1aaad8..2d4a96d90 100755 +--- a/freeipa.spec.in ++++ b/freeipa.spec.in +@@ -111,6 +111,15 @@ + %global httpd_version 2.4.41-6.1 + %endif + ++# BIND employs 'pkcs11' OpenSSL engine instead of native PKCS11 ++%if 0%{?fedora} >= 31 ++ %global with_bind_pkcs11 0 ++ %global openssl_pkcs11_version 0.4.10-6 ++ %global softhsm_version 2.5.0-4 ++%else ++ %global with_bind_pkcs11 1 ++%endif ++ + # Don't use Fedora's Python dependency generator on Fedora 30/rawhide yet. + # Some packages don't provide new dist aliases. + # https://docs.fedoraproject.org/en-US/packaging-guidelines/Python/ +@@ -481,8 +490,13 @@ Requires: %{name}-server = %{version}-%{release} + Requires: bind-dyndb-ldap >= 11.0-2 + Requires: bind >= 9.11.0-6.P2 + Requires: bind-utils >= 9.11.0-6.P2 ++%if 0%{?with_bind_pkcs11} + Requires: bind-pkcs11 >= 9.11.0-6.P2 + Requires: bind-pkcs11-utils >= 9.11.0-6.P2 ++%else ++Requires: softhsm >= %{softhsm_version} ++Requires: openssl-pkcs11 >= %{openssl_pkcs11_version} ++%endif + %if 0%{?fedora} >= 32 + # See https://bugzilla.redhat.com/show_bug.cgi?id=1825812 + Requires: opendnssec >= 2.1.6-5 +diff --git a/install/share/Makefile.am b/install/share/Makefile.am +index ae09afdc4..026d83035 100644 +--- a/install/share/Makefile.am ++++ b/install/share/Makefile.am +@@ -48,6 +48,8 @@ dist_app_DATA = \ + bind.ipa-ext.conf.template \ + bind.ipa-options-ext.conf.template \ + bind.named.conf.template \ ++ bind.openssl.cnf.template \ ++ bind.openssl.cryptopolicy.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/install/share/bind.openssl.cryptopolicy.cnf.template b/install/share/bind.openssl.cryptopolicy.cnf.template +new file mode 100644 +index 000000000..3fa5c492c +--- /dev/null ++++ b/install/share/bind.openssl.cryptopolicy.cnf.template +@@ -0,0 +1,21 @@ ++# OpenSSL configuration file ++# File generated by IPA instalation ++openssl_conf = openssl_init ++ ++[openssl_init] ++ssl_conf = ssl_configuration ++engines = engine_section ++ ++[ssl_configuration] ++system_default = crypto_policy ++ ++[crypto_policy] ++.include $CRYPTO_POLICY_FILE ++ ++[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 55999ee6a..631086945 100644 +--- a/ipaplatform/base/paths.py ++++ b/ipaplatform/base/paths.py +@@ -26,6 +26,7 @@ import os + + class BasePathNamespace: + BIN_HOSTNAMECTL = "/bin/hostnamectl" ++ CRYPTO_POLICY_OPENSSLCNF_FILE = None + ECHO = "/bin/echo" + FIPS_MODE_SETUP = "/usr/bin/fips-mode-setup" + GZIP = "/bin/gzip" +@@ -68,6 +69,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" +@@ -256,8 +258,6 @@ class BasePathNamespace: + IPA_REPLICA_CONNCHECK = "/usr/sbin/ipa-replica-conncheck" + IPA_RMKEYTAB = "/usr/sbin/ipa-rmkeytab" + IPACTL = "/usr/sbin/ipactl" +- NAMED = "/usr/sbin/named" +- NAMED_PKCS11 = "/usr/sbin/named-pkcs11" + CHRONYC = "/usr/bin/chronyc" + CHRONYD = "/usr/sbin/chronyd" + PKIDESTROY = "/usr/sbin/pkidestroy" +diff --git a/ipaplatform/fedora/constants.py b/ipaplatform/fedora/constants.py +index 7efa6e204..34ba0ce19 100644 +--- a/ipaplatform/fedora/constants.py ++++ b/ipaplatform/fedora/constants.py +@@ -27,4 +27,6 @@ class FedoraConstantsNamespace(RedHatConstantsNamespace): + if HAS_NFS_CONF: + SECURE_NFS_VAR = None + ++ NAMED_OPENSSL_ENGINE = "pkcs11" ++ + constants = FedoraConstantsNamespace() +diff --git a/ipaplatform/fedora/paths.py b/ipaplatform/fedora/paths.py +index 4e993c063..19cbb0e1d 100644 +--- a/ipaplatform/fedora/paths.py ++++ b/ipaplatform/fedora/paths.py +@@ -36,6 +36,8 @@ class FedoraPathNamespace(RedHatPathNamespace): + NAMED_CRYPTO_POLICY_FILE = "/etc/crypto-policies/back-ends/bind.config" + if HAS_NFS_CONF: + SYSCONFIG_NFS = '/etc/nfs.conf' ++ DNSSEC_OPENSSL_CONF = "/etc/ipa/dnssec/openssl.cnf" ++ DNSSEC_KEYFROMLABEL = "/usr/sbin/dnssec-keyfromlabel" + + + paths = FedoraPathNamespace() +diff --git a/ipaplatform/fedora/services.py b/ipaplatform/fedora/services.py +index 0778f624c..0669f4d20 100644 +--- a/ipaplatform/fedora/services.py ++++ b/ipaplatform/fedora/services.py +@@ -29,6 +29,8 @@ from ipaplatform.redhat import services as redhat_services + # Mappings from service names as FreeIPA code references to these services + # to their actual systemd service names + fedora_system_units = redhat_services.redhat_system_units.copy() ++fedora_system_units['named'] = fedora_system_units['named-regular'] ++fedora_system_units['named-conflict'] = fedora_system_units['named-pkcs11'] + + + # Service classes that implement Fedora-specific behaviour +@@ -41,6 +43,8 @@ class FedoraService(redhat_services.RedHatService): + # of specified name + + def fedora_service_class_factory(name, api=None): ++ if name in ['named', 'named-conflict']: ++ return FedoraService(name, api) + return redhat_services.redhat_service_class_factory(name, api) + + +diff --git a/ipaplatform/redhat/paths.py b/ipaplatform/redhat/paths.py +index 15bdef60f..eb4033a05 100644 +--- a/ipaplatform/redhat/paths.py ++++ b/ipaplatform/redhat/paths.py +@@ -31,6 +31,9 @@ from ipaplatform.base.paths import BasePathNamespace + + + class RedHatPathNamespace(BasePathNamespace): ++ CRYPTO_POLICY_OPENSSLCNF_FILE = ( ++ '/etc/crypto-policies/back-ends/opensslcnf.config' ++ ) + # https://docs.python.org/2/library/platform.html#cross-platform + if sys.maxsize > 2**32: + LIBSOFTHSM2_SO = BasePathNamespace.LIBSOFTHSM2_SO_64 +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() +- +- if running: +- self.restart() +- else: +- self.stop() +- +- self.named_regular.unmask() +- if named_regular_enabled: +- self.named_regular.enable() ++ self.disable() ++ self.stop() + +- 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..3d0d48a52 100644 +--- a/ipaserver/install/dnskeysyncinstance.py ++++ b/ipaserver/install/dnskeysyncinstance.py +@@ -4,11 +4,12 @@ + + from __future__ import print_function, absolute_import + +-import logging + import errno ++import grp ++import logging + import os + import pwd +-import grp ++import re + import shutil + import stat + +@@ -56,10 +57,10 @@ class DNSKeySyncInstance(service.Service): + keytab=paths.IPA_DNSKEYSYNCD_KEYTAB + ) + self.extra_config = [u'dnssecVersion 1', ] # DNSSEC enabled +- self.named_uid = None +- self.named_gid = None +- self.ods_uid = None +- self.ods_gid = None ++ self.named_uid = self.__get_named_uid() ++ self.named_gid = self.__get_named_gid() ++ self.ods_uid = self.__get_ods_uid() ++ self.ods_gid = self.__get_ods_gid() + + suffix = ipautil.dn_attribute_property('_suffix') + +@@ -67,12 +68,6 @@ class DNSKeySyncInstance(service.Service): + """ + Setting up correct permissions to allow write/read access for daemons + """ +- if self.named_uid is None: +- self.named_uid = self.__get_named_uid() +- +- if self.named_gid is None: +- self.named_gid = self.__get_named_gid() +- + if not os.path.exists(paths.BIND_LDAP_DNS_IPA_WORKDIR): + os.mkdir(paths.BIND_LDAP_DNS_IPA_WORKDIR, 0o770) + # dnssec daemons require to have access into the directory +@@ -133,20 +128,19 @@ class DNSKeySyncInstance(service.Service): + except KeyError: + raise RuntimeError("Named GID not found") + +- def __check_dnssec_status(self): +- self.named_uid = self.__get_named_uid() +- self.named_gid = self.__get_named_gid() +- ++ def __get_ods_uid(self): + try: +- self.ods_uid = pwd.getpwnam(constants.ODS_USER).pw_uid ++ return pwd.getpwnam(constants.ODS_USER).pw_uid + except KeyError: + raise RuntimeError("OpenDNSSEC UID not found") + ++ def __get_ods_gid(self): + try: +- self.ods_gid = grp.getgrnam(constants.ODS_GROUP).gr_gid ++ return grp.getgrnam(constants.ODS_GROUP).gr_gid + except KeyError: + raise RuntimeError("OpenDNSSEC GID not found") + ++ def __check_dnssec_status(self): + if not dns_container_exists(self.suffix): + raise RuntimeError("DNS container does not exist") + +@@ -164,10 +158,94 @@ class DNSKeySyncInstance(service.Service): + + self._ldap_mod("dnssec.ldif", {'SUFFIX': self.suffix, }) + +- def __setup_softhsm(self): +- assert self.ods_uid is not None +- assert self.named_gid is not None ++ def _are_named_options_configured(self, options): ++ """Check whether the sysconfig of named is patched ++ ++ Additional command line options for named are passed ++ via OPTIONS env variable. Since custom options can be ++ supplied by a vendor, at least, the base parsing of such ++ is required. ++ Current named command line options: ++ NS_MAIN_ARGS "46A:c:C:d:D:E:fFgi:lL:M:m:n:N:p:P:sS:t:T:U:u:vVx:X:" ++ If there are several same options the last passed wins. ++ """ ++ if options: ++ pattern = r"[ ]*-[a-zA-Z46]*E[ ]*(.*?)(?: |$)" ++ engines = re.findall(pattern, options) ++ if engines and engines[-1] == constants.NAMED_OPENSSL_ENGINE: ++ return True ++ ++ return False ++ ++ 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, ++ 'CRYPTO_POLICY_FILE': paths.CRYPTO_POLICY_OPENSSLCNF_FILE, ++ } ++ if paths.CRYPTO_POLICY_OPENSSLCNF_FILE is None: ++ opensslcnf_tmpl = "bind.openssl.cnf.template" ++ else: ++ opensslcnf_tmpl = "bind.openssl.cryptopolicy.cnf.template" ++ ++ named_openssl_txt = ipautil.template_file( ++ os.path.join(paths.USR_SHARE_IPA_DIR, opensslcnf_tmpl), ++ 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='=') ++ ++ options = directivesetter.get_directive( ++ paths.SYSCONFIG_NAMED, ++ constants.NAMED_OPTIONS_VAR, ++ separator="=" ++ ) or '' ++ if not self._are_named_options_configured(options): ++ engine_cmd = "-E {}".format(constants.NAMED_OPENSSL_ENGINE) ++ new_options = ' '.join([options, engine_cmd]) ++ directivesetter.set_directive( ++ sysconfig, ++ constants.NAMED_OPTIONS_VAR, new_options, ++ 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): + token_dir_exists = os.path.exists(paths.DNSSEC_TOKENS_DIR) + + # create dnssec directory +@@ -186,23 +264,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) +- +- # 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='=') ++ with open(paths.DNSSEC_SOFTHSM2_CONF, 'w') as f: ++ os.fchmod(f.fileno(), 0o644) ++ f.write(softhsm_conf_txt) ++ ++ # 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 +301,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 +441,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) + +@@ -389,7 +453,6 @@ class DNSKeySyncInstance(service.Service): + logger.error("DNSKeySync service already exists") + + def __setup_principal(self): +- assert self.ods_gid is not None + ipautil.remove_keytab(self.keytab) + installutils.kadmin_addprinc(self.principal) + +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/server/upgrade.py b/ipaserver/install/server/upgrade.py +index d532a1c0f..afd8bce5c 100644 +--- a/ipaserver/install/server/upgrade.py ++++ b/ipaserver/install/server/upgrade.py +@@ -527,6 +527,24 @@ def ca_initialize_hsm_state(ca): + ca.set_hsm_state(config) + + ++def dnssec_set_openssl_engine(dnskeysyncd): ++ """ ++ Setup OpenSSL engine for BIND ++ """ ++ if constants.NAMED_OPENSSL_ENGINE is None: ++ return False ++ ++ if sysupgrade.get_upgrade_state('dns', 'openssl_engine'): ++ return False ++ ++ logger.info('[Set OpenSSL engine for BIND]') ++ dnskeysyncd.setup_named_openssl_conf() ++ dnskeysyncd.setup_named_sysconfig() ++ dnskeysyncd.setup_ipa_dnskeysyncd_sysconfig() ++ sysupgrade.set_upgrade_state('dns', 'openssl_engine', True) ++ ++ return True ++ + + def certificate_renewal_update(ca, kra, ds, http): + """ +@@ -1425,7 +1443,10 @@ def upgrade_bind(fstore): + logger.info("DNS service is not configured") + return False + +- # get rid of old upgrade states ++ bind_switch_service(bind) ++ ++ # get rid of old states ++ bind_old_states(bind) + bind_old_upgrade_states() + + if bind.is_configured() and not bind.is_running(): +@@ -1451,6 +1472,38 @@ def upgrade_bind(fstore): + return changed + + ++def bind_switch_service(bind): ++ """ ++ Mask either named or named-pkcs11, we need to run only one, ++ running both can cause unexpected errors. ++ """ ++ named_conflict_name = bind.named_conflict.systemd_name ++ named_conflict_old = sysupgrade.get_upgrade_state('dns', 'conflict_named') ++ ++ # nothing changed ++ if named_conflict_old and named_conflict_old == named_conflict_name: ++ return False ++ ++ bind.switch_service() ++ ++ sysupgrade.set_upgrade_state('dns', 'conflict_named', named_conflict_name) ++ return True ++ ++ ++def bind_old_states(bind): ++ """Remove old states ++ """ ++ # no longer used states ++ old_states = [ ++ "enabled", ++ "running", ++ "named-regular-enabled", ++ "named-regular-running", ++ ] ++ for state in old_states: ++ bind.delete_state(state) ++ ++ + def bind_old_upgrade_states(): + """Remove old upgrade states + """ +@@ -1696,6 +1749,9 @@ def upgrade_configuration(): + if not dnskeysyncd.is_configured(): + dnskeysyncd.create_instance(fqdn, api.env.realm) + dnskeysyncd.start_dnskeysyncd() ++ else: ++ if dnssec_set_openssl_engine(dnskeysyncd): ++ dnskeysyncd.start_dnskeysyncd() + + cleanup_kdc(fstore) + cleanup_adtrust(fstore) +diff --git a/ipaserver/install/service.py b/ipaserver/install/service.py +index 3706bdd86..2123a17de 100644 +--- a/ipaserver/install/service.py ++++ b/ipaserver/install/service.py +@@ -533,6 +533,9 @@ class Service: + def get_state(self, key): + return self.sstore.get_state(self.service_name, key) + ++ def delete_state(self, key): ++ self.sstore.delete_state(self.service_name, key) ++ + def print_msg(self, message): + print_msg(message, self.output_fd) + +@@ -660,6 +663,7 @@ class Service: + ] + extra_config_opts.extend(config) + ++ self.unmask() + self.disable() + + set_service_entry_config( +diff --git a/ipatests/test_integration/test_commands.py b/ipatests/test_integration/test_commands.py +index fa6abd81e..e3c9f54a9 100644 +--- a/ipatests/test_integration/test_commands.py ++++ b/ipatests/test_integration/test_commands.py +@@ -989,7 +989,7 @@ class TestIPACommand(IntegrationTest): + + # get minimum version from current crypto-policy + openssl_cnf = self.master.get_file_contents( +- "/etc/crypto-policies/back-ends/opensslcnf.config", ++ paths.CRYPTO_POLICY_OPENSSLCNF_FILE, + encoding="utf-8" + ) + mo = re.search(r"MinProtocol\s*=\s*(TLSv[0-9.]+)", openssl_cnf) diff --git a/debian/patches/series b/debian/patches/series index 431ab1ccd..9662948cd 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -1,4 +1,5 @@ # upstreamed +pkcs11-openssl-for-bind.diff # not upstreamable