Skip to content

Commit 8076b11

Browse files
Jakujepirat89
authored andcommitted
OpenSslEnginesCheck: New actor to check OpenSSL engines (9->10)
The OpenSSL in EL 10 has deprecated engines in favor of providers. When the user upgrades to EL 10, the openssl configuration file will be changed to the EL 10 defaults and if the user depends on some engine for the system to boot or sshd to start, it might break and user needs to be aware of this. * The most common engine we shipped, pkcs11, has been removed from EL 10. The pkcs11-provider rpm will be installed automatically based on PES data if openssl-pkcs11 has been installed on the source system. However, user will be still informed they need to configure the system manually after the upgrade to use it. * The third-party engines could do anything so user needs to make sure neither of them is crucial for the minimal system to work. If they are still needed in the target system, they need to be configured again, but we recommend finding provider alternatives. JIRA: RHEL-78396 Signed-off-by: Jakub Jelen <[email protected]>
1 parent f8cc12e commit 8076b11

File tree

3 files changed

+329
-0
lines changed

3 files changed

+329
-0
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
from leapp.actors import Actor
2+
from leapp.exceptions import StopActorExecutionError
3+
from leapp.libraries.actor.opensslenginescheck import check_openssl_engines
4+
from leapp.libraries.stdlib import api
5+
from leapp.models import OpenSslConfig, Report
6+
from leapp.tags import ChecksPhaseTag, IPUWorkflowTag
7+
8+
9+
class OpenSslEnginesCheck(Actor):
10+
"""
11+
The OpenSSL in RHEL 10 has deprecated engines in favor of providers.
12+
13+
When they are kept in the default configuration file, they might
14+
not work as expected.
15+
16+
* The most common engine we shipped, pkcs11, has been removed from RHEL 10,
17+
which might cause failures to load the OpenSSL if it is hardcoded in the
18+
configuration file. However, as far as the /etc/pki/tls/openssl.cnf
19+
configuration file is replaced during the upgrade by the target default
20+
configuration, it should be ok to just inform user about that (see
21+
related actors in the system_upgrade_common repository).
22+
* Similarly user should be warned in case of third-party engines
23+
"""
24+
25+
name = 'open_ssl_engines_check'
26+
consumes = (OpenSslConfig,)
27+
produces = (Report,)
28+
tags = (IPUWorkflowTag, ChecksPhaseTag,)
29+
30+
def process(self):
31+
openssl_messages = self.consume(OpenSslConfig)
32+
config = next(openssl_messages, None)
33+
if list(openssl_messages):
34+
api.current_logger().warning('Unexpectedly received more than one OpenSslConfig message.')
35+
if not config:
36+
# NOTE: unexpected situation - putting the check just as a seatbelt
37+
# - not covered by unit-tests.
38+
raise StopActorExecutionError(
39+
'Could not check openssl configuration', details={'details': 'No OpenSslConfig facts found.'}
40+
)
41+
42+
# If the configuration file was not modified, it can not contain user changes
43+
if not config.modified:
44+
return
45+
46+
# The libp11 documentation has the following configuration snippet in the README:
47+
#
48+
# [openssl_init]
49+
# engines=engine_section
50+
#
51+
# [engine_section]
52+
# pkcs11 = pkcs11_section
53+
#
54+
# [pkcs11_section]
55+
# engine_id = pkcs11
56+
# dynamic_path = /usr/lib/ssl/engines/libpkcs11.so
57+
# MODULE_PATH = opensc-pkcs11.so
58+
# init = 0
59+
#
60+
# The `openssl_init` is required by OpenSSL 3.0 so we need to explore the section
61+
# pointed out by the `engines` key in there
62+
check_openssl_engines(config)
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
from leapp import reporting
2+
from leapp.libraries.stdlib import api
3+
4+
FMT_LIST_SEPARATOR = '\n - '
5+
RESOURCES = [
6+
reporting.RelatedResource('package', 'openssl'),
7+
reporting.RelatedResource('file', '/etc/pki/tls/openssl.cnf')
8+
]
9+
10+
11+
def _formatted_list_output(input_list, sep=FMT_LIST_SEPARATOR):
12+
return ['{}{}'.format(sep, item) for item in input_list]
13+
14+
15+
# NOTE: This is taken from the el8toel9 library in
16+
# repos/system_upgrade/el8toel9/actors/opensslconfigcheck/libraries/opensslconfigcheck.py
17+
def _normalize_key(key):
18+
"""
19+
Strip the part of the key before the first dot
20+
"""
21+
s = key.split('.', 1)
22+
if len(s) == 2:
23+
return s[1]
24+
return key
25+
26+
27+
def _key_equal(pair, key):
28+
"""
29+
Check the keys are equal in OpenSSL configuration semantics
30+
31+
The OpenSSL semantics ignores everything before the first dot to allow specifying
32+
something like following, where the first line would be otherwise normally ignored
33+
34+
TLS.MaxProtocol = TLSv1.3
35+
DTLS.MaxProtocol = DTLSv1.2
36+
"""
37+
if pair.key == key:
38+
return True
39+
return _normalize_key(pair.key) == key
40+
41+
42+
def _find_pair(block, key):
43+
"""
44+
Find key-value pair in the given configuration block
45+
46+
In the given configuration block (OpenSslConfigBlock) find a key-value with a given key.
47+
If multiple values match, only the last one is returned.
48+
"""
49+
res = None
50+
for pair in block.pairs:
51+
if _key_equal(pair, key):
52+
res = pair
53+
54+
return res
55+
56+
57+
def _openssl_find_block(config, name):
58+
"""
59+
In the given configuration file (OpenSslConfig) find a block with a given name
60+
"""
61+
for block in config.blocks:
62+
if block.name == name:
63+
return block
64+
65+
return None
66+
67+
68+
def check_openssl_engines(config):
69+
"""
70+
Check there are no engines configured in openssl.cnf
71+
72+
Report any detected openssl engines defined in /etc/pki/tls/openssl.cnf.
73+
"""
74+
init_block = _openssl_find_block(config, config.openssl_conf)
75+
if config.openssl_conf != 'openssl_init' or not init_block:
76+
api.current_logger().warning(
77+
'Non standard configuration in /etc/pki/tls/openssl.cnf: missing "openssl_init" section.'
78+
)
79+
return
80+
81+
engines_pair = _find_pair(init_block, 'engines')
82+
if not engines_pair:
83+
# No engines no problem
84+
return
85+
86+
engines_block = _openssl_find_block(config, engines_pair.value)
87+
if not engines_block:
88+
# No engines no problem
89+
return
90+
91+
enabled_engines = []
92+
# Iterate over engines directives -- they point to another block
93+
for engine in engines_block.pairs:
94+
name = engine.key
95+
engine_block = _openssl_find_block(config, engine.value)
96+
97+
# the engine is defined by name, but does not have a corresponding block
98+
if not engine_block:
99+
api.current_logger().debug(
100+
'The engine {} does not have corresponding configuration block.'
101+
.format(name)
102+
)
103+
continue
104+
105+
enabled_engines.append(name)
106+
107+
if enabled_engines:
108+
reporting.create_report([
109+
reporting.Title('Detected enabled deprecated engines in openssl.cnf'),
110+
reporting.Summary(
111+
'OpenSSL engines are deprecated since OpenSSL version 3.0'
112+
' and they are no longer supported nor available on the target'
113+
' RHEL 10 system. Any applications depending on OpenSSL engines'
114+
' might not work correctly on the target system and should be configured'
115+
' to use OpenSSL providers instead.'
116+
' The following OpenSSL engines are configured inside the /etc/pki/tls/openssl.cnf file:{}'
117+
.format(''.join(_formatted_list_output(enabled_engines)))
118+
),
119+
reporting.Remediation(hint=(
120+
'After the upgrade configure your system and applications'
121+
' to use OpenSSL providers instead of OpenSSL engines if needed.'
122+
)),
123+
reporting.Severity(reporting.Severity.MEDIUM),
124+
reporting.Groups([
125+
reporting.Groups.NETWORK,
126+
reporting.Groups.POST,
127+
reporting.Groups.SECURITY,
128+
reporting.Groups.SERVICES,
129+
]),
130+
] + RESOURCES)
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
from leapp.models import OpenSslConfig, OpenSslConfigBlock, OpenSslConfigPair, Report
2+
3+
4+
def test_actor_execution_empty(current_actor_context):
5+
current_actor_context.feed(
6+
OpenSslConfig(
7+
blocks=[],
8+
# modified=False, # default
9+
)
10+
)
11+
current_actor_context.run()
12+
assert not current_actor_context.consume(Report)
13+
14+
15+
def test_actor_execution_empty_modified(current_actor_context):
16+
current_actor_context.feed(
17+
OpenSslConfig(
18+
blocks=[],
19+
modified=True,
20+
)
21+
)
22+
current_actor_context.run()
23+
assert not current_actor_context.consume(Report)
24+
25+
26+
def test_actor_execution_default_modified(current_actor_context):
27+
current_actor_context.feed(
28+
OpenSslConfig(
29+
openssl_conf='openssl_init',
30+
blocks=[
31+
OpenSslConfigBlock(
32+
name='openssl_init',
33+
pairs=[
34+
OpenSslConfigPair(
35+
key='providers',
36+
value='provider_sect'
37+
),
38+
OpenSslConfigPair(
39+
key='ssl_conf',
40+
value='ssl_module'
41+
),
42+
OpenSslConfigPair(
43+
key='alg_section',
44+
value='evp_properties'
45+
)
46+
]
47+
),
48+
OpenSslConfigBlock(
49+
name='evp_properties',
50+
pairs=[]
51+
),
52+
OpenSslConfigBlock(
53+
name='provider_sect',
54+
pairs=[
55+
OpenSslConfigPair(
56+
key='default',
57+
value='default_sect'
58+
)
59+
]
60+
),
61+
OpenSslConfigBlock(
62+
name='default_sect',
63+
pairs=[
64+
OpenSslConfigPair(
65+
key='activate',
66+
value='1'
67+
)
68+
]
69+
),
70+
OpenSslConfigBlock(
71+
name='ssl_module',
72+
pairs=[
73+
OpenSslConfigPair(
74+
key='system_default',
75+
value='crypto_policy'
76+
)
77+
]
78+
),
79+
OpenSslConfigBlock(
80+
name='crypto_policy',
81+
pairs=[
82+
OpenSslConfigPair(
83+
key='.include',
84+
value='/etc/crypto-policies/back-ends/opensslcnf.config'
85+
)
86+
]
87+
),
88+
],
89+
modified=True,
90+
)
91+
)
92+
current_actor_context.run()
93+
assert not current_actor_context.consume(Report)
94+
95+
96+
def test_actor_execution_other_engine_modified(current_actor_context):
97+
# default, but removing contents unrelated for the checks
98+
current_actor_context.feed(
99+
OpenSslConfig(
100+
openssl_conf='openssl_init',
101+
blocks=[
102+
OpenSslConfigBlock(
103+
name='openssl_init',
104+
pairs=[
105+
OpenSslConfigPair(
106+
key='engines',
107+
value='engines_sect'
108+
)
109+
]
110+
),
111+
OpenSslConfigBlock(
112+
name='engines_sect',
113+
pairs=[
114+
OpenSslConfigPair(
115+
key='acme',
116+
value='acme_sect'
117+
)
118+
]
119+
),
120+
OpenSslConfigBlock(
121+
name='acme_sect',
122+
pairs=[
123+
OpenSslConfigPair(
124+
key='init',
125+
value='0'
126+
)
127+
]
128+
)
129+
],
130+
modified=True,
131+
)
132+
)
133+
current_actor_context.run()
134+
report = current_actor_context.consume(Report)
135+
assert report
136+
assert 'Detected enabled deprecated engines in openssl.cnf' in report[0].report['title']
137+
assert 'acme' in report[0].report['summary']

0 commit comments

Comments
 (0)