leapp-repository/0047-Distribution-agnostick-check-of-signed-packages-1-2.patch
Petr Stodulka e5599cfda4 RHEL 8.10: CTC2 candidate - 0
- Add detection of possible usage of OpenSSL IBMCA engine on IBM Z machines
- Add detection of modified /etc/pki/tls/openssl.cnf file
- Update the leapp upgrade data files
- Fix handling of symlinks under /etc/pki with relative paths specified
- Report custom actors and modifications of the upgrade tooling
- Requires xfsprogs and e2fsprogs to ensure that Ext4 and XFS tools are installed
- Bump leapp-repository-dependencies to 10
- Resolves: RHEL-1774, RHEL-16729
2024-01-12 20:45:10 +01:00

419 lines
19 KiB
Diff

From 1778818611efc961eda1e44894132689543cfcbe Mon Sep 17 00:00:00 2001
From: Evgeni Golov <evgeni@golov.de>
Date: Mon, 11 Dec 2023 10:45:22 +0100
Subject: [PATCH 47/60] Distribution agnostick check of signed packages [1/2]
The original detection covered only RHEL system, requiring rpms
to be signed by Red Hat (hardcoded). Also the model
InstalledRedHatSignedRPM didn't provide to much space for detection
of other distros.
The new solution checks RPMs signatures based on the detected
distribution ID (currently: rhel, centos). Fingerprints of GPG keys
and the packager string are stored under
repos/system_upgrade/common/files/distro/<distro>/signatures.json
where <distro> is the distribution id.
RedHatSignedRPMScanner is deprecated, replaced by DistributionSignedRPM
message. The original RedHatSignedRPMScanner will contain till the
removal just packages signed by RH.
The update of all other actors to consume DistributionSignedRPM is
covered in the next commit for the easier reading.
jira: OAMG-9824
Co-authored-by: Petr Stodulka <pstodulk@redhat.com>
---
.../distributionsignedrpmscanner/actor.py | 94 +++++++++++++++++++
.../test_distributionsignedrpmscanner.py} | 73 ++++++++++++++
.../actors/redhatsignedrpmscanner/actor.py | 75 ---------------
.../files/distro/centos/gpg-signatures.json | 8 ++
.../files/distro/rhel/gpg-signatures.json | 10 ++
.../common/models/installedrpm.py | 6 ++
6 files changed, 191 insertions(+), 75 deletions(-)
create mode 100644 repos/system_upgrade/common/actors/distributionsignedrpmscanner/actor.py
rename repos/system_upgrade/common/actors/{redhatsignedrpmscanner/tests/test_redhatsignedrpmscanner.py => distributionsignedrpmscanner/tests/test_distributionsignedrpmscanner.py} (68%)
delete mode 100644 repos/system_upgrade/common/actors/redhatsignedrpmscanner/actor.py
create mode 100644 repos/system_upgrade/common/files/distro/centos/gpg-signatures.json
create mode 100644 repos/system_upgrade/common/files/distro/rhel/gpg-signatures.json
diff --git a/repos/system_upgrade/common/actors/distributionsignedrpmscanner/actor.py b/repos/system_upgrade/common/actors/distributionsignedrpmscanner/actor.py
new file mode 100644
index 00000000..5772cb25
--- /dev/null
+++ b/repos/system_upgrade/common/actors/distributionsignedrpmscanner/actor.py
@@ -0,0 +1,94 @@
+import json
+import os
+
+from leapp.actors import Actor
+from leapp.exceptions import StopActorExecutionError
+from leapp.libraries.common import rhui
+from leapp.libraries.common.config import get_env
+from leapp.libraries.stdlib import api
+from leapp.models import DistributionSignedRPM, InstalledRedHatSignedRPM, InstalledRPM, InstalledUnsignedRPM
+from leapp.tags import FactsPhaseTag, IPUWorkflowTag
+from leapp.utils.deprecation import suppress_deprecation
+
+
+@suppress_deprecation(InstalledRedHatSignedRPM)
+class DistributionSignedRpmScanner(Actor):
+ """Provide data about installed RPM Packages signed by the distribution.
+
+ After filtering the list of installed RPM packages by signature, a message
+ with relevant data will be produced.
+ """
+
+ name = 'distribution_signed_rpm_scanner'
+ consumes = (InstalledRPM,)
+ produces = (DistributionSignedRPM, InstalledRedHatSignedRPM, InstalledUnsignedRPM,)
+ tags = (IPUWorkflowTag, FactsPhaseTag)
+
+ def process(self):
+ # TODO(pstodulk): refactor this function
+ # - move it to the private library
+ # - split it into several functions (so the main function stays small)
+ # FIXME(pstodulk): gpg-pubkey is handled wrong; it's not a real package
+ # and create FP report about unsigned RPMs. Keeping the fix for later.
+ distribution = self.configuration.os_release.release_id
+ distributions_path = api.get_common_folder_path('distro')
+
+ distribution_config = os.path.join(distributions_path, distribution, 'gpg-signatures.json')
+ if os.path.exists(distribution_config):
+ with open(distribution_config) as distro_config_file:
+ distro_config_json = json.load(distro_config_file)
+ distribution_keys = distro_config_json.get('keys', [])
+ distribution_packager = distro_config_json.get('packager', 'not-available')
+ else:
+ raise StopActorExecutionError(
+ 'Cannot find distribution signature configuration.',
+ details={'Problem': 'Distribution {} was not found in {}.'.format(distribution, distributions_path)})
+
+ signed_pkgs = DistributionSignedRPM()
+ rh_signed_pkgs = InstalledRedHatSignedRPM()
+ unsigned_pkgs = InstalledUnsignedRPM()
+
+ all_signed = get_env('LEAPP_DEVEL_RPMS_ALL_SIGNED', '0') == '1'
+
+ def has_distributionsig(pkg):
+ return any(key in pkg.pgpsig for key in distribution_keys)
+
+ def is_gpg_pubkey(pkg):
+ """
+ Check if gpg-pubkey pkg exists or LEAPP_DEVEL_RPMS_ALL_SIGNED=1
+
+ gpg-pubkey is not signed as it would require another package
+ to verify its signature
+ """
+ return ( # pylint: disable-msg=consider-using-ternary
+ pkg.name == 'gpg-pubkey'
+ and pkg.packager.startswith(distribution_packager)
+ or all_signed
+ )
+
+ def has_katello_prefix(pkg):
+ """Whitelist the katello package."""
+ return pkg.name.startswith('katello-ca-consumer')
+
+ whitelisted_cloud_pkgs = rhui.get_all_known_rhui_pkgs_for_current_upg()
+
+ for rpm_pkgs in self.consume(InstalledRPM):
+ for pkg in rpm_pkgs.items:
+ if any(
+ [
+ has_distributionsig(pkg),
+ is_gpg_pubkey(pkg),
+ has_katello_prefix(pkg),
+ pkg.name in whitelisted_cloud_pkgs,
+ ]
+ ):
+ signed_pkgs.items.append(pkg)
+ if distribution == 'rhel':
+ rh_signed_pkgs.items.append(pkg)
+ continue
+
+ unsigned_pkgs.items.append(pkg)
+
+ self.produce(signed_pkgs)
+ self.produce(rh_signed_pkgs)
+ self.produce(unsigned_pkgs)
diff --git a/repos/system_upgrade/common/actors/redhatsignedrpmscanner/tests/test_redhatsignedrpmscanner.py b/repos/system_upgrade/common/actors/distributionsignedrpmscanner/tests/test_distributionsignedrpmscanner.py
similarity index 68%
rename from repos/system_upgrade/common/actors/redhatsignedrpmscanner/tests/test_redhatsignedrpmscanner.py
rename to repos/system_upgrade/common/actors/distributionsignedrpmscanner/tests/test_distributionsignedrpmscanner.py
index 6652142e..a15ae173 100644
--- a/repos/system_upgrade/common/actors/redhatsignedrpmscanner/tests/test_redhatsignedrpmscanner.py
+++ b/repos/system_upgrade/common/actors/distributionsignedrpmscanner/tests/test_distributionsignedrpmscanner.py
@@ -3,12 +3,14 @@ import mock
from leapp.libraries.common import rpms
from leapp.libraries.common.config import mock_configs
from leapp.models import (
+ DistributionSignedRPM,
fields,
InstalledRedHatSignedRPM,
InstalledRPM,
InstalledUnsignedRPM,
IPUConfig,
Model,
+ OSRelease,
RPM
)
@@ -30,6 +32,7 @@ class MockModel(Model):
def test_no_installed_rpms(current_actor_context):
current_actor_context.run(config_model=mock_configs.CONFIG)
+ assert current_actor_context.consume(DistributionSignedRPM)
assert current_actor_context.consume(InstalledRedHatSignedRPM)
assert current_actor_context.consume(InstalledUnsignedRPM)
@@ -57,12 +60,74 @@ def test_actor_execution_with_signed_unsigned_data(current_actor_context):
current_actor_context.feed(InstalledRPM(items=installed_rpm))
current_actor_context.run(config_model=mock_configs.CONFIG)
+ assert current_actor_context.consume(DistributionSignedRPM)
+ assert len(current_actor_context.consume(DistributionSignedRPM)[0].items) == 5
assert current_actor_context.consume(InstalledRedHatSignedRPM)
assert len(current_actor_context.consume(InstalledRedHatSignedRPM)[0].items) == 5
assert current_actor_context.consume(InstalledUnsignedRPM)
assert len(current_actor_context.consume(InstalledUnsignedRPM)[0].items) == 4
+def test_actor_execution_with_signed_unsigned_data_centos(current_actor_context):
+ CENTOS_PACKAGER = 'CentOS BuildSystem <http://bugs.centos.org>'
+ config = mock_configs.CONFIG
+
+ config.os_release = OSRelease(
+ release_id='centos',
+ name='CentOS Linux',
+ pretty_name='CentOS Linux 7 (Core)',
+ version='7 (Core)',
+ version_id='7'
+ )
+
+ installed_rpm = [
+ RPM(name='sample01', version='0.1', release='1.sm01', epoch='1', packager=CENTOS_PACKAGER, arch='noarch',
+ pgpsig='RSA/SHA256, Mon 01 Jan 1970 00:00:00 AM -03, Key ID 24c6a8a7f4a80eb5'),
+ RPM(name='sample02', version='0.1', release='1.sm01', epoch='1', packager=CENTOS_PACKAGER, arch='noarch',
+ pgpsig='SOME_OTHER_SIG_X'),
+ RPM(name='sample03', version='0.1', release='1.sm01', epoch='1', packager=CENTOS_PACKAGER, arch='noarch',
+ pgpsig='RSA/SHA256, Mon 01 Jan 1970 00:00:00 AM -03, Key ID 05b555b38483c65d'),
+ RPM(name='sample04', version='0.1', release='1.sm01', epoch='1', packager=CENTOS_PACKAGER, arch='noarch',
+ pgpsig='SOME_OTHER_SIG_X'),
+ RPM(name='sample05', version='0.1', release='1.sm01', epoch='1', packager=CENTOS_PACKAGER, arch='noarch',
+ pgpsig='RSA/SHA256, Mon 01 Jan 1970 00:00:00 AM -03, Key ID 4eb84e71f2ee9d55'),
+ RPM(name='sample06', version='0.1', release='1.sm01', epoch='1', packager=CENTOS_PACKAGER, arch='noarch',
+ pgpsig='SOME_OTHER_SIG_X'),
+ RPM(name='sample07', version='0.1', release='1.sm01', epoch='1', packager=CENTOS_PACKAGER, arch='noarch',
+ pgpsig='RSA/SHA256, Mon 01 Jan 1970 00:00:00 AM -03, Key ID fd372689897da07a'),
+ RPM(name='sample08', version='0.1', release='1.sm01', epoch='1', packager=CENTOS_PACKAGER, arch='noarch',
+ pgpsig='SOME_OTHER_SIG_X'),
+ RPM(name='sample09', version='0.1', release='1.sm01', epoch='1', packager=CENTOS_PACKAGER, arch='noarch',
+ pgpsig='RSA/SHA256, Mon 01 Jan 1970 00:00:00 AM -03, Key ID 45689c882fa658e0')]
+
+ current_actor_context.feed(InstalledRPM(items=installed_rpm))
+ current_actor_context.run(config_model=config)
+ assert current_actor_context.consume(DistributionSignedRPM)
+ assert len(current_actor_context.consume(DistributionSignedRPM)[0].items) == 3
+ assert current_actor_context.consume(InstalledRedHatSignedRPM)
+ assert not current_actor_context.consume(InstalledRedHatSignedRPM)[0].items
+ assert current_actor_context.consume(InstalledUnsignedRPM)
+ assert len(current_actor_context.consume(InstalledUnsignedRPM)[0].items) == 6
+
+
+def test_actor_execution_with_unknown_distro(current_actor_context):
+ config = mock_configs.CONFIG
+
+ config.os_release = OSRelease(
+ release_id='myos',
+ name='MyOS Linux',
+ pretty_name='MyOS Linux 7 (Core)',
+ version='7 (Core)',
+ version_id='7'
+ )
+
+ current_actor_context.feed(InstalledRPM(items=[]))
+ current_actor_context.run(config_model=config)
+ assert not current_actor_context.consume(DistributionSignedRPM)
+ assert not current_actor_context.consume(InstalledRedHatSignedRPM)
+ assert not current_actor_context.consume(InstalledUnsignedRPM)
+
+
def test_all_rpms_signed(current_actor_context):
installed_rpm = [
RPM(name='sample01', version='0.1', release='1.sm01', epoch='1', packager=RH_PACKAGER, arch='noarch',
@@ -77,6 +142,8 @@ def test_all_rpms_signed(current_actor_context):
current_actor_context.feed(InstalledRPM(items=installed_rpm))
current_actor_context.run(config_model=mock_configs.CONFIG_ALL_SIGNED)
+ assert current_actor_context.consume(DistributionSignedRPM)
+ assert len(current_actor_context.consume(DistributionSignedRPM)[0].items) == 4
assert current_actor_context.consume(InstalledRedHatSignedRPM)
assert len(current_actor_context.consume(InstalledRedHatSignedRPM)[0].items) == 4
assert not current_actor_context.consume(InstalledUnsignedRPM)[0].items
@@ -95,6 +162,8 @@ def test_katello_pkg_goes_to_signed(current_actor_context):
current_actor_context.feed(InstalledRPM(items=installed_rpm))
current_actor_context.run(config_model=mock_configs.CONFIG_ALL_SIGNED)
+ assert current_actor_context.consume(DistributionSignedRPM)
+ assert len(current_actor_context.consume(DistributionSignedRPM)[0].items) == 1
assert current_actor_context.consume(InstalledRedHatSignedRPM)
assert len(current_actor_context.consume(InstalledRedHatSignedRPM)[0].items) == 1
assert not current_actor_context.consume(InstalledUnsignedRPM)[0].items
@@ -110,6 +179,8 @@ def test_gpg_pubkey_pkg(current_actor_context):
current_actor_context.feed(InstalledRPM(items=installed_rpm))
current_actor_context.run(config_model=mock_configs.CONFIG)
+ assert current_actor_context.consume(DistributionSignedRPM)
+ assert len(current_actor_context.consume(DistributionSignedRPM)[0].items) == 1
assert current_actor_context.consume(InstalledRedHatSignedRPM)
assert len(current_actor_context.consume(InstalledRedHatSignedRPM)[0].items) == 1
assert current_actor_context.consume(InstalledUnsignedRPM)
@@ -165,6 +236,8 @@ def test_has_package(current_actor_context):
current_actor_context.feed(InstalledRPM(items=installed_rpm))
current_actor_context.run(config_model=mock_configs.CONFIG)
+ assert rpms.has_package(DistributionSignedRPM, 'sample01', context=current_actor_context)
+ assert not rpms.has_package(DistributionSignedRPM, 'nosuchpackage', context=current_actor_context)
assert rpms.has_package(InstalledRedHatSignedRPM, 'sample01', context=current_actor_context)
assert not rpms.has_package(InstalledRedHatSignedRPM, 'nosuchpackage', context=current_actor_context)
assert rpms.has_package(InstalledUnsignedRPM, 'sample02', context=current_actor_context)
diff --git a/repos/system_upgrade/common/actors/redhatsignedrpmscanner/actor.py b/repos/system_upgrade/common/actors/redhatsignedrpmscanner/actor.py
deleted file mode 100644
index 41f9d343..00000000
--- a/repos/system_upgrade/common/actors/redhatsignedrpmscanner/actor.py
+++ /dev/null
@@ -1,75 +0,0 @@
-from leapp.actors import Actor
-from leapp.libraries.common import rhui
-from leapp.models import InstalledRedHatSignedRPM, InstalledRPM, InstalledUnsignedRPM
-from leapp.tags import FactsPhaseTag, IPUWorkflowTag
-
-
-class RedHatSignedRpmScanner(Actor):
- """Provide data about installed RPM Packages signed by Red Hat.
-
- After filtering the list of installed RPM packages by signature, a message
- with relevant data will be produced.
- """
-
- name = 'red_hat_signed_rpm_scanner'
- consumes = (InstalledRPM,)
- produces = (InstalledRedHatSignedRPM, InstalledUnsignedRPM,)
- tags = (IPUWorkflowTag, FactsPhaseTag)
-
- def process(self):
- RH_SIGS = ['199e2f91fd431d51',
- '5326810137017186',
- '938a80caf21541eb',
- 'fd372689897da07a',
- '45689c882fa658e0']
-
- signed_pkgs = InstalledRedHatSignedRPM()
- unsigned_pkgs = InstalledUnsignedRPM()
-
- env_vars = self.configuration.leapp_env_vars
- # if we start upgrade with LEAPP_DEVEL_RPMS_ALL_SIGNED=1, we consider
- # all packages to be signed
- all_signed = [
- env
- for env in env_vars
- if env.name == 'LEAPP_DEVEL_RPMS_ALL_SIGNED' and env.value == '1'
- ]
-
- def has_rhsig(pkg):
- return any(key in pkg.pgpsig for key in RH_SIGS)
-
- def is_gpg_pubkey(pkg):
- """Check if gpg-pubkey pkg exists or LEAPP_DEVEL_RPMS_ALL_SIGNED=1
-
- gpg-pubkey is not signed as it would require another package
- to verify its signature
- """
- return ( # pylint: disable-msg=consider-using-ternary
- pkg.name == 'gpg-pubkey'
- and pkg.packager.startswith('Red Hat, Inc.')
- or all_signed
- )
-
- def has_katello_prefix(pkg):
- """Whitelist the katello package."""
- return pkg.name.startswith('katello-ca-consumer')
-
- whitelisted_cloud_pkgs = rhui.get_all_known_rhui_pkgs_for_current_upg()
-
- for rpm_pkgs in self.consume(InstalledRPM):
- for pkg in rpm_pkgs.items:
- if any(
- [
- has_rhsig(pkg),
- is_gpg_pubkey(pkg),
- has_katello_prefix(pkg),
- pkg.name in whitelisted_cloud_pkgs,
- ]
- ):
- signed_pkgs.items.append(pkg)
- continue
-
- unsigned_pkgs.items.append(pkg)
-
- self.produce(signed_pkgs)
- self.produce(unsigned_pkgs)
diff --git a/repos/system_upgrade/common/files/distro/centos/gpg-signatures.json b/repos/system_upgrade/common/files/distro/centos/gpg-signatures.json
new file mode 100644
index 00000000..30e329ee
--- /dev/null
+++ b/repos/system_upgrade/common/files/distro/centos/gpg-signatures.json
@@ -0,0 +1,8 @@
+{
+ "keys": [
+ "24c6a8a7f4a80eb5",
+ "05b555b38483c65d",
+ "4eb84e71f2ee9d55"
+ ],
+ "packager": "CentOS"
+}
diff --git a/repos/system_upgrade/common/files/distro/rhel/gpg-signatures.json b/repos/system_upgrade/common/files/distro/rhel/gpg-signatures.json
new file mode 100644
index 00000000..eccf0106
--- /dev/null
+++ b/repos/system_upgrade/common/files/distro/rhel/gpg-signatures.json
@@ -0,0 +1,10 @@
+{
+ "keys": [
+ "199e2f91fd431d51",
+ "5326810137017186",
+ "938a80caf21541eb",
+ "fd372689897da07a",
+ "45689c882fa658e0"
+ ],
+ "packager": "Red Hat, Inc."
+}
diff --git a/repos/system_upgrade/common/models/installedrpm.py b/repos/system_upgrade/common/models/installedrpm.py
index 5a632b03..cc9fd508 100644
--- a/repos/system_upgrade/common/models/installedrpm.py
+++ b/repos/system_upgrade/common/models/installedrpm.py
@@ -1,5 +1,6 @@
from leapp.models import fields, Model
from leapp.topics import SystemInfoTopic
+from leapp.utils.deprecation import deprecated
class RPM(Model):
@@ -21,6 +22,11 @@ class InstalledRPM(Model):
items = fields.List(fields.Model(RPM), default=[])
+class DistributionSignedRPM(InstalledRPM):
+ pass
+
+
+@deprecated(since='2024-01-31', message='Replaced by DistributionSignedRPM')
class InstalledRedHatSignedRPM(InstalledRPM):
pass
--
2.43.0