-
Notifications
You must be signed in to change notification settings - Fork 153
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
OpenSslEnginesCheck: New actor for OpenSSL engines (9->10)
Signed-off-by: Jakub Jelen <[email protected]>
- Loading branch information
Showing
3 changed files
with
393 additions
and
0 deletions.
There are no files selected for viewing
57 changes: 57 additions & 0 deletions
57
repos/system_upgrade/el9toel10/actors/opensslenginescheck/actor.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
from leapp.actors import Actor | ||
from leapp.exceptions import StopActorExecutionError | ||
from leapp.libraries.actor.opensslenginescheck import check_openssl_engines | ||
from leapp.libraries.stdlib import api | ||
from leapp.models import OpenSslConfig, Report | ||
from leapp.tags import ChecksPhaseTag, IPUWorkflowTag | ||
|
||
|
||
class OpenSslEnginesCheck(Actor): | ||
""" | ||
The OpenSSL in RHEL 10 has deprecated engines in favor of providers. | ||
When they are kept in the default configuration file, they might | ||
not work as expected. | ||
* The most common engine we shipped, pkcs11, has been removed from RHEL 10, | ||
which might cause failures to load the OpenSSL if it is hardcoded in the | ||
configuration file. It needs to be removed before update from the configuration. | ||
* For third-party engines, we should at least warn the user about deprecation. | ||
""" | ||
|
||
name = 'open_ssl_engines_check' | ||
consumes = (OpenSslConfig,) | ||
produces = (Report,) | ||
tags = (IPUWorkflowTag, ChecksPhaseTag,) | ||
|
||
def process(self): | ||
openssl_messages = self.consume(OpenSslConfig) | ||
config = next(openssl_messages, None) | ||
if list(openssl_messages): | ||
api.current_logger().warning('Unexpectedly received more than one OpenSslConfig message.') | ||
if not config: | ||
raise StopActorExecutionError( | ||
'Could not check openssl configuration', details={'details': 'No OpenSslConfig facts found.'} | ||
) | ||
|
||
# If the configuration file was not modified, it can not contain user changes | ||
if not config.modified: | ||
return | ||
|
||
# The libp11 documentation has the following configuration snippet in the README: | ||
# | ||
# [openssl_init] | ||
# engines=engine_section | ||
# | ||
# [engine_section] | ||
# pkcs11 = pkcs11_section | ||
# | ||
# [pkcs11_section] | ||
# engine_id = pkcs11 | ||
# dynamic_path = /usr/lib/ssl/engines/libpkcs11.so | ||
# MODULE_PATH = opensc-pkcs11.so | ||
# init = 0 | ||
# | ||
# The `openssl_init` is required by OpenSSL 3.0 so we need to explore the section | ||
# pointed out by the `engines` key in there | ||
check_openssl_engines(config) |
154 changes: 154 additions & 0 deletions
154
repos/system_upgrade/el9toel10/actors/opensslenginescheck/libraries/opensslenginescheck.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
from leapp import reporting | ||
from leapp.libraries.stdlib import api | ||
|
||
|
||
# FIXME: This is taken from the el8toel9 library in | ||
# repos/system_upgrade/el8toel9/actors/opensslconfigcheck/libraries/opensslconfigcheck.py | ||
def _normalize_key(key): | ||
""" | ||
Strip the part of the key before the first dot | ||
""" | ||
s = key.split(".", 1) | ||
if len(s) == 2: | ||
return s[1] | ||
return key | ||
|
||
|
||
def _key_equal(pair, key): | ||
""" | ||
Check the keys are equal in OpenSSL configuration semantics | ||
The OpenSSL semantics ignores everything before the first dot to allow specifying | ||
something like following, where the first line would be otherwise normally ignored | ||
TLS.MaxProtocol = TLSv1.3 | ||
DTLS.MaxProtocol = DTLSv1.2 | ||
""" | ||
if pair.key == key: | ||
return True | ||
return _normalize_key(pair.key) == key | ||
|
||
|
||
def _find_pair(block, key): | ||
""" | ||
Find key-value pair in the given configuration block | ||
In the given configuration block (OpenSslConfigBlock) find a key-value with a given key. | ||
If multiple values match, only the last one is returned. | ||
""" | ||
res = None | ||
for pair in block.pairs: | ||
if _key_equal(pair, key): | ||
res = pair | ||
|
||
return res | ||
|
||
|
||
def _openssl_find_block(config, name): | ||
""" | ||
In the given configuration file (OpenSslConfig) find a block with a given name | ||
""" | ||
for block in config.blocks: | ||
if block.name == name: | ||
return block | ||
|
||
return None | ||
|
||
|
||
resources = [ | ||
reporting.RelatedResource('package', 'openssl'), | ||
reporting.RelatedResource('file', '/etc/pki/tls/openssl.cnf') | ||
] | ||
|
||
|
||
def check_openssl_engines(config): | ||
""" | ||
Check there are no engines configured in openssl.cnf | ||
In case the of the pkcs11 engine, stop the upgrade as the package was removed from RHEL 10. | ||
In case of other (usually third-party) engines, just warn about them being deprecated, | ||
and suggest removal. | ||
""" | ||
init_block = _openssl_find_block(config, "openssl_init") | ||
if config.openssl_conf != "openssl_init" or not init_block: | ||
reporting.create_report([ | ||
reporting.Title('Non-standard configuration of openssl.cnf'), | ||
reporting.Summary( | ||
'The OpenSSL configuration file `/etc/pki/tls/openssl.cnf` does not contain ' | ||
'expected initialization (openssl_conf = openssl_init key-value pair).' | ||
), | ||
reporting.Remediation( | ||
'The openssl.cnf file needs to contain the following initialization: ' | ||
'`openssl_conf = openssl_init` The `openssl_conf` now contains {} or ' | ||
'the `[ openssl_init ]` block is missing. '.format(config.openssl_conf) | ||
), | ||
reporting.Groups([reporting.Groups.INHIBITOR]), | ||
reporting.Severity(reporting.Severity.HIGH), | ||
reporting.Groups([ | ||
reporting.Groups.SECURITY, | ||
reporting.Groups.NETWORK, | ||
reporting.Groups.SERVICES | ||
]), | ||
] + resources) | ||
return | ||
|
||
engines_pair = _find_pair(init_block, "engines") | ||
if not engines_pair: | ||
# No engines no problem | ||
return | ||
|
||
engines_block = _openssl_find_block(config, engines_pair.value) | ||
if not engines_block: | ||
# No engines no problem | ||
return | ||
|
||
enabled_engines = [] | ||
# Iterate over engines directives -- they point to another block | ||
for engine in engines_block.pairs: | ||
name = engine.key | ||
engine_block = _openssl_find_block(config, engine.value) | ||
|
||
# the engine is defined by name, but does not have a corresponding block | ||
if not engine_block: | ||
api.current_logger().debug("The engine {} does not have corresponding configuration block." | ||
.format(name)) | ||
continue | ||
|
||
enabled_engines.append(name) | ||
|
||
if "pkcs11" in enabled_engines: | ||
reporting.create_report([ | ||
reporting.Title('There is pkcs11 engine configured in openssl.cnf'), | ||
reporting.Summary( | ||
'The OpenSSL configuration file `/etc/pki/tls/openssl.cnf` contains the ' | ||
'initialization of pkcs11 engin. The pkcs11 engine was removed from RHEL 10 ' | ||
'and replaced with pkcs11-provider. Before continuing the update, please remove ' | ||
'the pkcs11 engine configuration.' | ||
), | ||
reporting.Groups([reporting.Groups.INHIBITOR]), | ||
reporting.Severity(reporting.Severity.HIGH), | ||
reporting.Groups([ | ||
reporting.Groups.SECURITY, | ||
reporting.Groups.NETWORK, | ||
reporting.Groups.SERVICES | ||
]), | ||
] + resources) | ||
# do not report it again below if it is the only one | ||
enabled_engines.remove("pkcs11") | ||
|
||
if enabled_engines: | ||
reporting.create_report([ | ||
reporting.Title('There are enabled engines in openssl.cnf'), | ||
reporting.Summary( | ||
'The OpenSSL configuration file `/etc/pki/tls/openssl.cnf` contains the ' | ||
'following enabled engines: {}. They are deprecated in OpenSSL 3.0, will ' | ||
'not work in the following versions. '.format(', '.join(enabled_engines)) | ||
), | ||
reporting.Severity(reporting.Severity.LOW), | ||
reporting.Groups([ | ||
reporting.Groups.SECURITY, | ||
reporting.Groups.NETWORK, | ||
reporting.Groups.SERVICES | ||
]), | ||
] + resources) |
182 changes: 182 additions & 0 deletions
182
..._upgrade/el9toel10/actors/opensslenginescheck/tests/component_test_opensslenginescheck.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
from leapp.models import OpenSslConfig, OpenSslConfigBlock, OpenSslConfigPair, Report | ||
|
||
|
||
def test_actor_execution_empty(current_actor_context): | ||
current_actor_context.feed( | ||
OpenSslConfig( | ||
blocks=[], | ||
# modified=False, # default | ||
) | ||
) | ||
current_actor_context.run() | ||
assert not current_actor_context.consume(Report) | ||
|
||
|
||
def test_actor_execution_empty_modified(current_actor_context): | ||
current_actor_context.feed( | ||
OpenSslConfig( | ||
blocks=[], | ||
modified=True, | ||
) | ||
) | ||
current_actor_context.run() | ||
r = current_actor_context.consume(Report) | ||
assert r | ||
assert 'Non-standard configuration of openssl.cnf' in r[0].report['title'] | ||
|
||
|
||
def test_actor_execution_default_modified(current_actor_context): | ||
current_actor_context.feed( | ||
OpenSslConfig( | ||
openssl_conf='openssl_init', | ||
blocks=[ | ||
OpenSslConfigBlock( | ||
name="openssl_init", | ||
pairs=[ | ||
OpenSslConfigPair( | ||
key="providers", | ||
value="provider_sect" | ||
), | ||
OpenSslConfigPair( | ||
key="ssl_conf", | ||
value="ssl_module" | ||
), | ||
OpenSslConfigPair( | ||
key="alg_section", | ||
value="evp_properties" | ||
) | ||
] | ||
), | ||
OpenSslConfigBlock( | ||
name="evp_properties", | ||
pairs=[] | ||
), | ||
OpenSslConfigBlock( | ||
name="provider_sect", | ||
pairs=[ | ||
OpenSslConfigPair( | ||
key="default", | ||
value="default_sect" | ||
) | ||
] | ||
), | ||
OpenSslConfigBlock( | ||
name="default_sect", | ||
pairs=[ | ||
OpenSslConfigPair( | ||
key="activate", | ||
value="1" | ||
) | ||
] | ||
), | ||
OpenSslConfigBlock( | ||
name="ssl_module", | ||
pairs=[ | ||
OpenSslConfigPair( | ||
key="system_default", | ||
value="crypto_policy" | ||
) | ||
] | ||
), | ||
OpenSslConfigBlock( | ||
name="crypto_policy", | ||
pairs=[ | ||
OpenSslConfigPair( | ||
key=".include", | ||
value="/etc/crypto-policies/back-ends/opensslcnf.config" | ||
) | ||
] | ||
), | ||
], | ||
modified=True, | ||
) | ||
) | ||
current_actor_context.run() | ||
assert not current_actor_context.consume(Report) | ||
|
||
|
||
def test_actor_execution_pkcs11_engine_modified(current_actor_context): | ||
# default, but removing contents unrelated for the checks | ||
current_actor_context.feed( | ||
OpenSslConfig( | ||
openssl_conf='openssl_init', | ||
blocks=[ | ||
OpenSslConfigBlock( | ||
name="openssl_init", | ||
pairs=[ | ||
OpenSslConfigPair( | ||
key="engines", | ||
value="engines_sect" | ||
) | ||
] | ||
), | ||
OpenSslConfigBlock( | ||
name="engines_sect", | ||
pairs=[ | ||
OpenSslConfigPair( | ||
key="pkcs11", | ||
value="pkcs11_sect" | ||
) | ||
] | ||
), | ||
OpenSslConfigBlock( | ||
name="pkcs11_sect", | ||
pairs=[ | ||
OpenSslConfigPair( | ||
key="dynamic_path", | ||
value="/usr/lib64/engines-3/pkcs11.so" | ||
) | ||
] | ||
) | ||
], | ||
modified=True, | ||
) | ||
) | ||
current_actor_context.run() | ||
r = current_actor_context.consume(Report) | ||
assert r | ||
assert 'There is pkcs11 engine configured in openssl.cnf' in r[0].report['title'] | ||
|
||
|
||
def test_actor_execution_other_engine_modified(current_actor_context): | ||
# default, but removing contents unrelated for the checks | ||
current_actor_context.feed( | ||
OpenSslConfig( | ||
openssl_conf='openssl_init', | ||
blocks=[ | ||
OpenSslConfigBlock( | ||
name="openssl_init", | ||
pairs=[ | ||
OpenSslConfigPair( | ||
key="engines", | ||
value="engines_sect" | ||
) | ||
] | ||
), | ||
OpenSslConfigBlock( | ||
name="engines_sect", | ||
pairs=[ | ||
OpenSslConfigPair( | ||
key="acme", | ||
value="acme_sect" | ||
) | ||
] | ||
), | ||
OpenSslConfigBlock( | ||
name="acme_sect", | ||
pairs=[ | ||
OpenSslConfigPair( | ||
key="init", | ||
value="0" | ||
) | ||
] | ||
) | ||
], | ||
modified=True, | ||
) | ||
) | ||
current_actor_context.run() | ||
r = current_actor_context.consume(Report) | ||
assert r | ||
assert 'There are enabled engines in openssl.cnf' in r[0].report['title'] | ||
assert 'acme' in r[0].report['summary'] |