From ace726cb83320d7fcb051751591817fd419a8f6b Mon Sep 17 00:00:00 2001 From: Alexander Bokovoy Date: Wed, 6 Nov 2024 09:59:23 +0200 Subject: [PATCH] Use OpenSSL provider with BIND for Fedora 41+ and RHEL10+ OpenSSL Engine API is deprecated and ability to compile against it is removed in RHEL10. OpenSSL provider API is the future. Fedora 41+ also defaults to OpenSSL provider. With pkcs11-provider, the same PKCS#11 modules can be loaded transparently like with OpenSSL engines. Thus, we can update configuration to use the provider API. TODO: - dnssec-keyfromlabel does not work without engine, needs backport from bind 9.20 Fixes: https://pagure.io/freeipa/issue/9696 Signed-off-by: Alexander Bokovoy --- freeipa.spec.in | 12 +++- install/share/Makefile.am | 2 + .../share/bind.openssl.provider.cnf.template | 19 +++++++ .../bind.openssl.provider.crp.cnf.template | 25 +++++++++ ipaplatform/base/constants.py | 1 + ipaplatform/fedora/constants.py | 9 ++- ipaplatform/rhel/constants.py | 7 ++- ipaserver/dnssec/bindmgr.py | 21 ++++--- ipaserver/install/dnskeysyncinstance.py | 55 +++++++++++++++---- ipaserver/install/server/upgrade.py | 12 ++-- 10 files changed, 136 insertions(+), 27 deletions(-) create mode 100644 install/share/bind.openssl.provider.cnf.template create mode 100644 install/share/bind.openssl.provider.crp.cnf.template diff --git a/freeipa.spec.in b/freeipa.spec.in index 72d7013a6c49873f4a59734c684c6c5510e669d0..3f6b133eee4ec40193b618882ad0813971beb5ec 100755 --- a/freeipa.spec.in +++ b/freeipa.spec.in @@ -158,12 +158,20 @@ # BIND employs 'pkcs11' OpenSSL engine instead of native PKCS11 # Fedora 31+ uses OpenSSL engine, as well as Fedora ELN (RHEL9) -%if 0%{?fedora} || 0%{?rhel} >= 9 +# Howevever, Fedora 40+ and RHEL10+ use OpenSSL provider +%if 0%{?fedora} < 40 || 0%{?rhel} == 9 %global openssl_pkcs11_version 0.4.10-6 + %global openssl_pkcs11_name openssl-pkcs11 %global softhsm_version 2.5.0-4 +%else +%if 0%{?fedora} >= 40 || 0%{?rhel} >= 10 + %global openssl_pkcs11_version 0.3 + %global openssl_pkcs11_name pkcs11-provider + %global softhsm_version 2.6.1 %else %global with_bind_pkcs11 1 %endif +%endif %if 0%{?rhel} == 8 # Make sure to use PKI versions that work with 389-ds fix for https://github.com/389ds/389-ds-base/issues/4609 @@ -623,7 +631,7 @@ Requires: bind-dnssec-utils >= %{bind_version} Requires: bind-pkcs11 >= %{bind_version} %else Requires: softhsm >= %{softhsm_version} -Requires: openssl-pkcs11 >= %{openssl_pkcs11_version} +Requires: %{openssl_pkcs11_name} >= %{openssl_pkcs11_version} %endif # See https://bugzilla.redhat.com/show_bug.cgi?id=1825812 # RHEL 8.3+ and Fedora 32+ have 2.1 diff --git a/install/share/Makefile.am b/install/share/Makefile.am index 24664ca3bacb01fa4c57e9d7a5ea4ab48cfbdd90..0adebf8a3b0e01dbf62fe4b86190e60a3fbfea3b 100644 --- a/install/share/Makefile.am +++ b/install/share/Makefile.am @@ -50,6 +50,8 @@ dist_app_DATA = \ bind.named.conf.template \ bind.openssl.cnf.template \ bind.openssl.cryptopolicy.cnf.template \ + bind.openssl.provider.cnf.template \ + bind.openssl.provider.crp.cnf.template \ certmap.conf.template \ kdc.conf.template \ kdc_extensions.template \ diff --git a/install/share/bind.openssl.provider.cnf.template b/install/share/bind.openssl.provider.cnf.template new file mode 100644 index 0000000000000000000000000000000000000000..1bd5599cd32f9601416cbaca815dc73fca22b560 --- /dev/null +++ b/install/share/bind.openssl.provider.cnf.template @@ -0,0 +1,19 @@ +# OpenSSL configuration file +# File generated by IPA instalation +openssl_conf = openssl_init + +[openssl_init] +providers = provider_section + +[provider_sect] +default = default_sect +pkcs11 = pkcs11_bind_sect + +[default_sect] +activate = 1 + +[pkcs11_bind_sect] +pkcs11-module-path = $SOFTHSM_MODULE +pkcs11-module-token-pin = file:$SOFTHSM_PIN +activate = 1 + diff --git a/install/share/bind.openssl.provider.crp.cnf.template b/install/share/bind.openssl.provider.crp.cnf.template new file mode 100644 index 0000000000000000000000000000000000000000..b52175e8f9971fa1a25a6c1c7a7121b2fc4c8c36 --- /dev/null +++ b/install/share/bind.openssl.provider.crp.cnf.template @@ -0,0 +1,25 @@ +# OpenSSL configuration file +# File generated by IPA instalation +openssl_conf = openssl_init + +[openssl_init] +ssl_conf = ssl_configuration +providers = provider_sect + +[ssl_configuration] +system_default = crypto_policy + +[crypto_policy] +.include $CRYPTO_POLICY_FILE + +[provider_sect] +default = default_sect +pkcs11 = pkcs11_bind_sect + +[default_sect] +activate = 1 + +[pkcs11_bind_sect] +pkcs11-module-path = $SOFTHSM_MODULE +pkcs11-module-token-pin = file:$SOFTHSM_PIN +activate = 1 diff --git a/ipaplatform/base/constants.py b/ipaplatform/base/constants.py index 1689efe52466f00fd8b014f720e1d21ebdbf2504..3f607ecbf961fbd78d78e05bcc1af3cd15a549d5 100644 --- a/ipaplatform/base/constants.py +++ b/ipaplatform/base/constants.py @@ -120,6 +120,7 @@ class BaseConstantsNamespace: NAMED_DATA_DIR = "data/" NAMED_OPTIONS_VAR = "OPTIONS" NAMED_OPENSSL_ENGINE = None + NAMED_OPENSSL_PROVIDER = None NAMED_ZONE_COMMENT = "" PKI_USER = User("pkiuser") PKI_GROUP = Group("pkiuser") diff --git a/ipaplatform/fedora/constants.py b/ipaplatform/fedora/constants.py index 896e6f60737a904b06ac5fba6c1d1711577c79ec..78a53db28755d5394441ed6d5350648c80de54df 100644 --- a/ipaplatform/fedora/constants.py +++ b/ipaplatform/fedora/constants.py @@ -19,6 +19,10 @@ from ipaplatform.osinfo import osinfo # Fedora 29 has both HAS_NFS_CONF = osinfo.version_number >= (30,) +# Fedora 40 and later deprecated OpenSSL engine and recommend using OpenSSL +# provider API. +HAS_OPENSSL_PROVIDER = osinfo.version_number >= (40,) + __all__ = ("constants", "User", "Group") @@ -32,6 +36,9 @@ class FedoraConstantsNamespace(RedHatConstantsNamespace): if HAS_NFS_CONF: SECURE_NFS_VAR = None - NAMED_OPENSSL_ENGINE = "pkcs11" + if HAS_OPENSSL_PROVIDER: + NAMED_OPENSSL_PROVIDER = True + else: + NAMED_OPENSSL_ENGINE = "pkcs11" constants = FedoraConstantsNamespace() diff --git a/ipaplatform/rhel/constants.py b/ipaplatform/rhel/constants.py index bc8c65a5d35af9afd27bc728768e49cd937e79a5..f4b50352190811db9dc780e3cec9d02cc0cab354 100644 --- a/ipaplatform/rhel/constants.py +++ b/ipaplatform/rhel/constants.py @@ -18,8 +18,11 @@ from ipaplatform.osinfo import osinfo # RHEL 8 uses /etc/nfs.conf HAS_NFS_CONF = osinfo.version_number >= (8,) # RHEL 9 uses pkcs11 as openssl engine -HAS_PKCS11_OPENSSL_ENGINE = osinfo.version_number >= (9,) +HAS_PKCS11_OPENSSL_ENGINE = osinfo.version_number == (9,) +# RHEL 10 and later deprecated OpenSSL engine and recommend using OpenSSL +# provider API. +HAS_OPENSSL_PROVIDER = osinfo.version_number >= (10,) __all__ = ("constants", "User", "Group") @@ -31,5 +34,7 @@ class RHELConstantsNamespace(RedHatConstantsNamespace): SECURE_NFS_VAR = None if HAS_PKCS11_OPENSSL_ENGINE: NAMED_OPENSSL_ENGINE = "pkcs11" + if HAS_OPENSSL_PROVIDER: + NAMED_OPENSSL_PROVIDER = True constants = RHELConstantsNamespace() diff --git a/ipaserver/dnssec/bindmgr.py b/ipaserver/dnssec/bindmgr.py index 0c79cc03d404f0fb54bc3c6ab591206127c5870c..aeb8b919c64361fd8175366827fecba9705af3c3 100644 --- a/ipaserver/dnssec/bindmgr.py +++ b/ipaserver/dnssec/bindmgr.py @@ -121,17 +121,24 @@ class BINDMgr: assert attrs.get('idnsseckeyzone', [b'FALSE'])[0] == b'TRUE', \ b'object %s is not a DNS zone key' % attrs['dn'] - uri = b"%s;pin-source=%s" % ( - attrs['idnsSecKeyRef'][0], - paths.DNSSEC_SOFTHSM_PIN.encode('utf-8') - ) + uri = None + if platformconstants.NAMED_OPENSSL_ENGINE is not None: + uri = "%s;pin-source=%s" % ( + attrs['idnsSecKeyRef'][0], + paths.DNSSEC_SOFTHSM_PIN.encode('utf-8') + ) + elif platformconstants.NAMED_OPENSSL_PROVIDER is not None: + uri = "%s;token=%s" % ( + attrs['idnsSecKeyRef'][0], + ipalib.constants.SOFTHSM_DNSSEC_TOKEN_LABEL.encode('utf-8') + ) cmd = [ paths.DNSSEC_KEYFROMLABEL, - '-E', 'pkcs11', '-K', workdir, - '-a', attrs['idnsSecAlgorithm'][0], - '-l', uri + '-a', attrs['idnsSecAlgorithm'][0].encode('utf-8'), ] + if uri is not None: + cmd.extend(['-l', uri]) cmd.extend(self.dates2params(attrs)) if attrs.get('idnsSecKeySep', [b'FALSE'])[0].upper() == b'TRUE': cmd.extend(['-f', 'KSK']) diff --git a/ipaserver/install/dnskeysyncinstance.py b/ipaserver/install/dnskeysyncinstance.py index 36524655265130fca910eceb63fd4793ccc60d48..1979a472dd882a70cb0a41d782689debc66017a9 100644 --- a/ipaserver/install/dnskeysyncinstance.py +++ b/ipaserver/install/dnskeysyncinstance.py @@ -155,21 +155,36 @@ class DNSKeySyncInstance(service.Service): return False def setup_named_openssl_conf(self): + opensslcnf_tmpl = None + conf_file_dict = { + 'CRYPTO_POLICY_FILE': paths.CRYPTO_POLICY_OPENSSLCNF_FILE, + 'SOFTHSM_MODULE': paths.LIBSOFTHSM2_SO, + 'SOFTHSM_PIN': paths.DNSSEC_SOFTHSM_PIN, + } 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, - } + # Traditional configuration using OpenSSL engine API + # requires openssl-pkcs11 engine to load PKCS#11 token + # provided by SoftHSMv2 + conf_file_dict['OPENSSL_ENGINE'] = constants.NAMED_OPENSSL_ENGINE if paths.CRYPTO_POLICY_OPENSSLCNF_FILE is None: opensslcnf_tmpl = "bind.openssl.cnf.template" else: opensslcnf_tmpl = "bind.openssl.cryptopolicy.cnf.template" + elif constants.NAMED_OPENSSL_PROVIDER is not None: + # OpenSSL provider API is preferred and requires + # pkcs11-provider to load PKCS#11 token provided by SoftHSMv2 + if paths.CRYPTO_POLICY_OPENSSLCNF_FILE is None: + opensslcnf_tmpl = "bind.openssl.provider.cnf.template" + else: + opensslcnf_tmpl = "bind.openssl.provider.crp.cnf.template" + else: + conf_file_dict = None + if opensslcnf_tmpl is not None and conf_file_dict 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 named_openssl_txt = ipautil.template_file( os.path.join(paths.USR_SHARE_IPA_DIR, opensslcnf_tmpl), conf_file_dict @@ -189,7 +204,8 @@ class DNSKeySyncInstance(service.Service): 'SOFTHSM2_CONF', paths.DNSSEC_SOFTHSM2_CONF, quotes=False, separator='=') - if constants.NAMED_OPENSSL_ENGINE is not None: + if any([constants.NAMED_OPENSSL_ENGINE is not None, + constants.NAMED_OPENSSL_PROVIDER is not None]): directivesetter.set_directive( sysconfig, 'OPENSSL_CONF', paths.DNSSEC_OPENSSL_CONF, @@ -200,9 +216,23 @@ class DNSKeySyncInstance(service.Service): constants.NAMED_OPTIONS_VAR, separator="=" ) or '' - if not self._are_named_options_configured(options): + new_options = None + if all([constants.NAMED_OPENSSL_ENGINE is not None, + not self._are_named_options_configured(options)]): engine_cmd = "-E {}".format(constants.NAMED_OPENSSL_ENGINE) new_options = ' '.join([options, engine_cmd]) + # Remove '-E pkcs11' from the options in the OpenSSL provider case + if all([constants.NAMED_OPENSSL_ENGINE is None, + self._are_named_options_configured(options)]): + lst_options = options.split() + try: + idx = lst_options.index('-E') + lst_options.pop(idx) + lst_options.pop(idx) + new_options = ' '.join(lst_options) + except ValueError: + pass + if new_options is not None: directivesetter.set_directive( sysconfig, constants.NAMED_OPTIONS_VAR, new_options, @@ -216,7 +246,8 @@ class DNSKeySyncInstance(service.Service): 'SOFTHSM2_CONF', paths.DNSSEC_SOFTHSM2_CONF, quotes=False, separator='=') - if constants.NAMED_OPENSSL_ENGINE is not None: + if any([constants.NAMED_OPENSSL_ENGINE is not None, + constants.NAMED_OPENSSL_PROVIDER is not None]): directivesetter.set_directive( sysconfig, 'OPENSSL_CONF', paths.DNSSEC_OPENSSL_CONF, diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py index fb71df81a6bf8ecbb1631ca8f0a5fe55cc222782..e2aabb2845602aacda1ca3289b7d7e338bd2dba3 100644 --- a/ipaserver/install/server/upgrade.py +++ b/ipaserver/install/server/upgrade.py @@ -549,15 +549,19 @@ def ca_initialize_hsm_state(ca): def dnssec_set_openssl_engine(dnskeysyncd): """ - Setup OpenSSL engine for BIND + Setup OpenSSL engine or provider for BIND """ - if constants.NAMED_OPENSSL_ENGINE is None: + if all([constants.NAMED_OPENSSL_ENGINE is None, + constants.NAMED_OPENSSL_PROVIDER is None]): return False - if sysupgrade.get_upgrade_state('dns', 'openssl_engine'): + # Nothing to do if we are using OpenSSL engine already and not on the OS + # that requires OpenSSL provider instead. + if all([sysupgrade.get_upgrade_state('dns', 'openssl_engine'), + constants.NAMED_OPENSSL_PROVIDER is None]): return False - logger.info('[Set OpenSSL engine for BIND]') + logger.info('[Set OpenSSL engine or provider for BIND]') dnskeysyncd.setup_named_openssl_conf() dnskeysyncd.setup_named_sysconfig() dnskeysyncd.setup_ipa_dnskeysyncd_sysconfig() -- 2.47.0