Skip to content

Commit 1a7977d

Browse files
committed
Keep crypto-policies actors from el8toel9 in el9toel10
There are two actors related to crypto-policies in el8toel9 and their corresponing models. They are still relevant for el9toel10 upgrade path but we cannot easily store them in common because they are not relevant for el7toel8 upgrade path. Therefore I am copying mode and both actors from el8toel9 to el9toel10 with a tiny documentation change in cryptopoliciescheck actor. Signed-off-by: Ondrej Moris <[email protected]>
1 parent c5accf4 commit 1a7977d

File tree

9 files changed

+428
-0
lines changed

9 files changed

+428
-0
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from leapp.actors import Actor
2+
from leapp.libraries.actor import cryptopoliciescheck
3+
from leapp.models import CryptoPolicyInfo, Report, TargetUserSpacePreupgradeTasks
4+
from leapp.tags import ChecksPhaseTag, IPUWorkflowTag
5+
6+
7+
class CryptoPoliciesCheck(Actor):
8+
"""
9+
This actor consumes previously gathered information about crypto policies on the source
10+
system and does two things:
11+
12+
* warns user if the custom/legacy policy is used and whether there is time to review it
13+
* prepares the container by making sure it will have installed the tools for managing
14+
crypto policies and the custom policies are copied over to the intermediate and target
15+
systems
16+
"""
17+
18+
name = 'crypto_policies_check'
19+
consumes = (CryptoPolicyInfo,)
20+
produces = (TargetUserSpacePreupgradeTasks, Report,)
21+
tags = (IPUWorkflowTag, ChecksPhaseTag,)
22+
23+
def process(self):
24+
cryptopoliciescheck.process(self.consume(CryptoPolicyInfo))
25+
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
from leapp import reporting
2+
from leapp.exceptions import StopActorExecutionError
3+
from leapp.libraries.stdlib import api
4+
from leapp.models import CopyFile, TargetUserSpacePreupgradeTasks
5+
6+
7+
def _get_files_to_copy(cpi):
8+
return [f.path for f in cpi.custom_policies + cpi.custom_modules]
9+
10+
11+
def process(cpi_messages):
12+
cpi = next(cpi_messages, None)
13+
if list(cpi_messages):
14+
api.current_logger().warning('Unexpectedly received more than one CryptoPolicyInfo message.')
15+
if not cpi:
16+
raise StopActorExecutionError(
17+
'Could not check crypto policies status',
18+
details={'details': 'No CryptoPolicyInfo facts found.'}
19+
)
20+
21+
if cpi.current_policy != 'DEFAULT':
22+
# If we have to change the crypto policies inside the target userspace container,
23+
# we need update-crypto-policies script inside as well as potential custom policies files
24+
files = [CopyFile(src=f) for f in _get_files_to_copy(cpi)]
25+
api.produce(TargetUserSpacePreupgradeTasks(install_rpms=['crypto-policies-scripts'],
26+
copy_files=files))
27+
28+
# When non-default crypto policy is used, it might be outdated. Recommend user to revisit.
29+
# exceptions are here the FIPS and FUTURE policies, which are more future-proof.
30+
if cpi.current_policy in ('FIPS', 'FUTURE'):
31+
return
32+
reporting.create_report([
33+
reporting.Title('System-wide crypto policy is set to non-DEFAULT policy'),
34+
reporting.Summary((
35+
"The system-wide crypto policies are set to `{}` value. This might be"
36+
" outdated decision, the custom crypto policy might be outdated and no"
37+
" longer meeting the security standards. Please, review the current crypto"
38+
" policies settings. If this is intentional and up-to-date, you can ignore"
39+
" this message. The custom crypto policy will be configured on the updated"
40+
" system."
41+
).format(cpi.current_policy)),
42+
reporting.Severity(reporting.Severity.MEDIUM),
43+
reporting.Groups([reporting.Groups.SECURITY, reporting.Groups.SANITY]),
44+
reporting.Remediation(hint="Review the current policy in /etc/crypto-policies/state/CURRENT.pol"),
45+
reporting.RelatedResource('package', 'crypto-policies'),
46+
)
47+
])
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
from leapp.models import (
2+
CopyFile,
3+
CryptoPolicyInfo,
4+
CustomCryptoPolicy,
5+
CustomCryptoPolicyModule,
6+
Report,
7+
TargetUserSpacePreupgradeTasks
8+
)
9+
10+
11+
def test_actor_execution_default(current_actor_context):
12+
current_actor_context.feed(
13+
CryptoPolicyInfo(
14+
current_policy="DEFAULT",
15+
custom_policies=[],
16+
custom_modules=[],
17+
)
18+
)
19+
current_actor_context.run()
20+
assert not current_actor_context.consume(TargetUserSpacePreupgradeTasks)
21+
22+
23+
def test_actor_execution_legacy(current_actor_context):
24+
current_actor_context.feed(
25+
CryptoPolicyInfo(
26+
current_policy="LEGACY",
27+
custom_policies=[],
28+
custom_modules=[],
29+
)
30+
)
31+
current_actor_context.run()
32+
33+
assert current_actor_context.consume(TargetUserSpacePreupgradeTasks)
34+
u = current_actor_context.consume(TargetUserSpacePreupgradeTasks)[0]
35+
assert u.install_rpms == ['crypto-policies-scripts']
36+
assert u.copy_files == []
37+
38+
assert current_actor_context.consume(Report)
39+
40+
41+
def test_actor_execution_custom(current_actor_context):
42+
current_actor_context.feed(
43+
CryptoPolicyInfo(
44+
current_policy="CUSTOM:SHA2",
45+
custom_policies=[
46+
CustomCryptoPolicy(name='CUSTOM', path='/etc/crypto-policies/policies/CUSTOM.pol'),
47+
],
48+
custom_modules=[
49+
CustomCryptoPolicyModule(name='SHA2', path='/etc/crypto-policies/policies/modules/SHA2.pmod'),
50+
],
51+
)
52+
)
53+
current_actor_context.run()
54+
55+
assert current_actor_context.consume(TargetUserSpacePreupgradeTasks)
56+
u = current_actor_context.consume(TargetUserSpacePreupgradeTasks)[0]
57+
assert u.install_rpms == ['crypto-policies-scripts']
58+
assert u.copy_files == [
59+
CopyFile(src='/etc/crypto-policies/policies/CUSTOM.pol'),
60+
CopyFile(src='/etc/crypto-policies/policies/modules/SHA2.pmod'),
61+
]
62+
63+
assert current_actor_context.consume(Report)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from leapp.libraries.actor.cryptopoliciescheck import _get_files_to_copy
2+
from leapp.models import CryptoPolicyInfo, CustomCryptoPolicy, CustomCryptoPolicyModule
3+
4+
5+
def test_get_files_to_copy():
6+
cpi = CryptoPolicyInfo(current_policy="DEFAULT", custom_policies=[], custom_modules=[])
7+
assert _get_files_to_copy(cpi) == []
8+
9+
cpi.custom_policies.append(CustomCryptoPolicy(name="CUSTOM", path="/path/to/CUSTOM.pol"))
10+
assert _get_files_to_copy(cpi) == ["/path/to/CUSTOM.pol"]
11+
12+
cpi.custom_modules.append(CustomCryptoPolicyModule(name="FIX", path="/path/to/FIX.mpol"))
13+
assert _get_files_to_copy(cpi) == ["/path/to/CUSTOM.pol", "/path/to/FIX.mpol"]
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from leapp.actors import Actor
2+
from leapp.libraries.actor import scancryptopolicies
3+
from leapp.models import CryptoPolicyInfo
4+
from leapp.tags import FactsPhaseTag, IPUWorkflowTag
5+
6+
7+
class ScanCryptoPolicies(Actor):
8+
"""
9+
Scan information about system wide set crypto policies including:
10+
* current crypto policy
11+
* installed custom crypto policies
12+
13+
This information is, later in the process useful for the following:
14+
* copy the custom crypto policies files
15+
* notify user about the current setting and to review whether the policy still makes sense
16+
* it might be outdated and no longer meet the best security practices
17+
* if it is based on system policy such as DEFAULT, it might cause unexpected changes
18+
"""
19+
20+
name = 'scancryptopolicies'
21+
consumes = ()
22+
produces = (CryptoPolicyInfo,)
23+
tags = (IPUWorkflowTag, FactsPhaseTag)
24+
25+
def process(self):
26+
scancryptopolicies.process()
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import os
2+
3+
from leapp.exceptions import StopActorExecutionError
4+
from leapp.libraries.stdlib import api, run
5+
from leapp.models import CryptoPolicyInfo, CustomCryptoPolicy, CustomCryptoPolicyModule
6+
7+
CRYPTO_CURRENT_STATE_FILE = '/etc/crypto-policies/state/current'
8+
CRYPTO_POLICIES_POLICY_DIRS = ('/etc/crypto-policies/policies',
9+
'/usr/share/crypto-policies/policies',)
10+
CRYPTO_POLICIES_MODULES_DIRS = ('/etc/crypto-policies/policies/modules',
11+
'/usr/share/crypto-policies/policies/modules',)
12+
13+
14+
def read_current_policy(file):
15+
if not os.path.exists(file):
16+
# NOTE(pstodulk) just seatbelt, I do not expect the file is not present
17+
# skipping tests
18+
raise StopActorExecutionError(
19+
'File not found: {}'.format(file),
20+
details={'details:': 'Cannot check the current set crypto policies.'}
21+
)
22+
current = 'DEFAULT'
23+
with open(file) as fp:
24+
current = fp.read().strip()
25+
return current
26+
27+
28+
def _get_name_from_file(file):
29+
"""This is just stripping the path and the extension"""
30+
base = os.path.basename(file)
31+
return os.path.splitext(base)[0]
32+
33+
34+
def find_rpm_untracked(files):
35+
"""Check if the list of files is tracked by RPM"""
36+
if not files:
37+
return []
38+
try:
39+
res = run(['rpm', '-Vf', ] + files, split=True, checked=False)
40+
except OSError as err:
41+
error = 'Failed to invoke rpm to check untracked files: {}'.format(str(err))
42+
api.current_logger().error(error)
43+
return []
44+
45+
# return only untracked files from the list
46+
out = []
47+
for file in files:
48+
exp = "file {} is not owned by any package".format(file)
49+
if exp in res['stdout']:
50+
out.append(file)
51+
return out
52+
53+
54+
def read_policy_dirs(dirs, obj, extension):
55+
"""List files with given extension in given directories. Returns only the ones that are not tracked by RPM"""
56+
files = []
57+
# find all policy files
58+
for d in dirs:
59+
for file in os.listdir(d):
60+
file = os.path.join(d, file)
61+
if not os.path.isfile(file) or not file.endswith(extension):
62+
continue
63+
files.append(file)
64+
# now, check which are not tracked by RPM:
65+
files = find_rpm_untracked(files)
66+
out = []
67+
for file in files:
68+
name = _get_name_from_file(file)
69+
out.append(obj(name=name, path=file))
70+
71+
return out
72+
73+
74+
def process():
75+
current = read_current_policy(CRYPTO_CURRENT_STATE_FILE)
76+
77+
policies = read_policy_dirs(CRYPTO_POLICIES_POLICY_DIRS, CustomCryptoPolicy, ".pol")
78+
modules = read_policy_dirs(CRYPTO_POLICIES_MODULES_DIRS, CustomCryptoPolicyModule, ".pmod")
79+
80+
api.produce(CryptoPolicyInfo(current_policy=current,
81+
custom_policies=policies,
82+
custom_modules=modules))
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from leapp.models import CryptoPolicyInfo
2+
3+
4+
def test_actor_execution(current_actor_context):
5+
current_actor_context.run()
6+
assert current_actor_context.consume(CryptoPolicyInfo)
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import os
2+
import tempfile
3+
4+
import pytest
5+
6+
from leapp.exceptions import StopActorExecutionError
7+
from leapp.libraries.actor.scancryptopolicies import (
8+
_get_name_from_file,
9+
find_rpm_untracked,
10+
read_current_policy,
11+
read_policy_dirs
12+
)
13+
from leapp.models import CustomCryptoPolicy, CustomCryptoPolicyModule
14+
15+
NOFILE = "/tmp/non-existing-file-should-not-really-be-here"
16+
17+
18+
def test_get_name_from_file():
19+
assert _get_name_from_file("/path/name.extension") == "name"
20+
assert _get_name_from_file("/othername.e") == "othername"
21+
assert _get_name_from_file("/other.name.e") == "other.name"
22+
assert _get_name_from_file("/some/long/path/other.name") == "other"
23+
assert _get_name_from_file("/some/long/path/no_extension") == "no_extension"
24+
25+
26+
def test_find_rpm_untracked(current_actor_context):
27+
# this is tracked
28+
files = ["/tmp/"]
29+
assert find_rpm_untracked(files) == []
30+
files = ["/etc/crypto-policies/config"]
31+
assert find_rpm_untracked(files) == []
32+
33+
# the tempfile is not tracked by RPM
34+
with tempfile.NamedTemporaryFile(delete=False) as f:
35+
files = [f.name]
36+
assert find_rpm_untracked(files) == [f.name]
37+
38+
# not existing files are ignored
39+
files = [NOFILE]
40+
assert find_rpm_untracked(files) == []
41+
42+
# combinations should yield expected results too
43+
files = ["/tmp", f.name, NOFILE]
44+
assert find_rpm_untracked(files) == [f.name]
45+
# regardless the order
46+
files = [NOFILE, f.name, "/tmp"]
47+
assert find_rpm_untracked(files) == [f.name]
48+
49+
50+
def test_read_current_policy():
51+
with pytest.raises(StopActorExecutionError):
52+
assert read_current_policy(NOFILE)
53+
54+
with tempfile.NamedTemporaryFile(delete=False) as f:
55+
f.write(b'DEFAULT:SHA1')
56+
f.flush()
57+
assert read_current_policy(f.name) == "DEFAULT:SHA1"
58+
59+
f.seek(0)
60+
f.write(b' DEFAULT:SHA1 \n\n ')
61+
f.flush()
62+
assert read_current_policy(f.name) == "DEFAULT:SHA1"
63+
64+
65+
def test_read_policy_dirs(current_actor_context):
66+
with tempfile.TemporaryDirectory() as dir1:
67+
# empty
68+
files = read_policy_dirs([dir1], CustomCryptoPolicy, ".pol")
69+
assert files == []
70+
71+
# first policy module
72+
path1 = os.path.join(dir1, "policy.mpol")
73+
with open(path1, "x") as f:
74+
f.write('test')
75+
files = read_policy_dirs([dir1], CustomCryptoPolicy, ".pol")
76+
assert files == []
77+
files = read_policy_dirs([dir1], CustomCryptoPolicyModule, ".mpol")
78+
assert files == [CustomCryptoPolicyModule(name="policy", path=path1)]
79+
80+
with tempfile.TemporaryDirectory() as dir2:
81+
files = read_policy_dirs([dir1], CustomCryptoPolicy, ".pol")
82+
assert files == []
83+
files = read_policy_dirs([dir1, dir2], CustomCryptoPolicyModule, ".mpol")
84+
assert files == [CustomCryptoPolicyModule(name="policy", path=path1)]
85+
86+
# first policy file
87+
path2 = os.path.join(dir2, "mypolicy.pol")
88+
with open(path2, "x") as f:
89+
f.write('test2')
90+
# second policy file
91+
path3 = os.path.join(dir2, "other.pol")
92+
with open(path3, "x") as f:
93+
f.write('test3')
94+
95+
files = read_policy_dirs([dir1, dir2], dict, ".pol")
96+
assert len(files) == 2
97+
assert dict(name="mypolicy", path=path2) in files
98+
assert dict(name="other", path=path3) in files
99+
files = read_policy_dirs([dir1, dir2], CustomCryptoPolicyModule, ".mpol")
100+
assert files == [CustomCryptoPolicyModule(name="policy", path=path1)]
101+
102+
files = read_policy_dirs([dir1], CustomCryptoPolicy, ".pol")
103+
assert files == []
104+
files = read_policy_dirs([dir1], CustomCryptoPolicyModule, ".mpol")
105+
assert files == [CustomCryptoPolicyModule(name="policy", path=path1)]

0 commit comments

Comments
 (0)