forked from rpms/leapp-repository
e5599cfda4
- 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
419 lines
19 KiB
Diff
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
|
|
|