Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor handling of GPG keys into separate library and verify no unexpected keys are installed in ApplicationsPhase #1101

Merged
merged 3 commits into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ help:
@echo " PR=7 SUFFIX='my_additional_suffix' make <target>"
@echo " MR=6 COPR_CONFIG='path/to/the/config/copr/file' make <target>"
@echo " ACTOR=<actor> TEST_LIBS=y make test"
@echo " BUILD_CONTAINER=el7 make build_container"
@echo " BUILD_CONTAINER=rhel7 make build_container"
@echo " TEST_CONTAINER=f34 make test_container"
@echo " CONTAINER_TOOL=docker TEST_CONTAINER=rhel7 make test_container_no_lint"
@echo ""
Expand Down
23 changes: 23 additions & 0 deletions repos/system_upgrade/common/actors/gpgpubkeycheck/actor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from leapp.actors import Actor
from leapp.libraries.actor import gpgpubkeycheck
from leapp.models import TrustedGpgKeys
from leapp.reporting import Report
from leapp.tags import ApplicationsPhaseTag, IPUWorkflowTag


class GpgPubkeyCheck(Actor):
"""
Checks no unexpected GPG keys were installed during the upgrade.

This should be mostly sanity check and this should not happen
unless something went very wrong, regardless the gpgcheck was
used (default) or not (with --no-gpgcheck option).
"""

name = 'gpg_pubkey_check'
consumes = (TrustedGpgKeys,)
produces = (Report,)
tags = (IPUWorkflowTag, ApplicationsPhaseTag,)

def process(self):
gpgpubkeycheck.process()
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
from leapp import reporting
from leapp.libraries.common.gpg import is_nogpgcheck_set
from leapp.libraries.common.rpms import get_installed_rpms
from leapp.libraries.stdlib import api
from leapp.models import TrustedGpgKeys

FMT_LIST_SEPARATOR = '\n - '


def _get_installed_fps_tuple():
"""
Return list of tuples (fingerprint, packager).
"""
installed_fps_tuple = []
rpms = get_installed_rpms()
for rpm in rpms:
rpm = rpm.strip()
if not rpm:
continue
try:
# NOTE: pgpsig is (none) for 'gpg-pubkey' entries
name, version, dummy_release, dummy_epoch, packager, dummy_arch, dummy_pgpsig = rpm.split('|')
except ValueError as e:
# NOTE: it's seatbelt, but if it happens, seeing loong list of errors
# will let us know earlier that we missed something really
api.current_logger().error('Cannot perform the check of installed GPG keys after the upgrade.')
api.current_logger().error('Cannot parse rpm output: {}'.format(e))
continue
if name != 'gpg-pubkey':
continue
installed_fps_tuple.append((version, packager))
return installed_fps_tuple


def _report_cannot_check_keys(installed_fps):
# NOTE: in this case, it's expected there will be always some GPG keys present
summary = (
'Cannot perform the check of GPG keys installed in the RPM DB'
' due to missing facts (TrustedGpgKeys) supposed to be generated'
' in the start of the upgrade process on the original system.'
' Unexpected unexpected installed GPG keys could be e.g. a mark of'
' a malicious attempt to hijack the upgrade process.'
' The list of all GPG keys in RPM DB:{sep}{key_list}'
.format(
sep=FMT_LIST_SEPARATOR,
key_list=FMT_LIST_SEPARATOR.join(installed_fps)
)
)
hint = (
'Verify the installed GPG keys are expected.'
)
groups = [
reporting.Groups.POST,
reporting.Groups.REPOSITORY,
reporting.Groups.SECURITY
]
reporting.create_report([
reporting.Title('Cannot perform the check of installed GPG keys after the upgrade.'),
reporting.Summary(summary),
reporting.Severity(reporting.Severity.HIGH),
reporting.Groups(groups),
reporting.Remediation(hint=hint),
])


def _report_unexpected_keys(unexpected_fps):
summary = (
'The system contains unexpected GPG keys after upgrade.'
' This can be caused e.g. by a manual intervention'
' or by malicious attempt to hijack the upgrade process.'
' The unexpected keys are the following:'
' {sep}{key_list}'
.format(
sep=FMT_LIST_SEPARATOR,
key_list=FMT_LIST_SEPARATOR.join(unexpected_fps)
)
)
hint = (
'Verify the installed GPG keys are expected.'
)
groups = [
reporting.Groups.POST,
reporting.Groups.REPOSITORY,
reporting.Groups.SECURITY
]
reporting.create_report([
reporting.Title('Detected unexpected GPG keys after the upgrade.'),
reporting.Summary(summary),
reporting.Severity(reporting.Severity.HIGH),
reporting.Groups(groups),
reporting.Remediation(hint=hint),
])


def process():
"""
Verify the system does not have any unexpected gpg keys installed
If the --no-gpgcheck option is used, this is skipped as we can not
guarantee that what was installed came from trusted source
"""

if is_nogpgcheck_set():
api.current_logger().warning('The --nogpgcheck option is used: Skipping the check of installed GPG keys.')
return

installed_fps_tuple = _get_installed_fps_tuple()

try:
trusted_gpg_keys = next(api.consume(TrustedGpgKeys))
except StopIteration:
# unexpected (bug) situation; keeping as seatbelt for the security aspect
installed_fps = ['{fp}: {packager}'.format(fp=fp, packager=packager) for fp, packager in installed_fps_tuple]
_report_cannot_check_keys(installed_fps)
return

trusted_fps = [key.fingerprint for key in trusted_gpg_keys.items]
unexpected_fps = []
for fp, packager in installed_fps_tuple:
if fp not in trusted_fps:
unexpected_fps.append('{fp}: {packager}'.format(fp=fp, packager=packager))

if unexpected_fps:
_report_unexpected_keys(unexpected_fps)
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
from leapp.libraries.actor import missinggpgkey
from leapp.models import (
DNFWorkaround,
InstalledRPM,
TargetUserSpaceInfo,
TMPTargetRepositoriesFacts,
TrustedGpgKeys,
UsedTargetRepositories
)
from leapp.reporting import Report
Expand All @@ -28,7 +28,7 @@ class MissingGpgKeysInhibitor(Actor):

name = 'missing_gpg_keys_inhibitor'
consumes = (
InstalledRPM,
TrustedGpgKeys,
TMPTargetRepositoriesFacts,
TargetUserSpaceInfo,
UsedTargetRepositories,
Expand Down
Loading
Loading