forked from rpms/leapp-repository
		
	
		
			
				
	
	
		
			210 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			210 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From eeb4f99f57c67937ea562fce11fd5607470ae0a6 Mon Sep 17 00:00:00 2001
 | |
| From: Petr Stodulka <pstodulk@redhat.com>
 | |
| Date: Fri, 22 Apr 2022 00:20:15 +0200
 | |
| Subject: [PATCH] [IPU 8 -> 9] Migrate blacklisted CAs (hotfix)
 | |
| 
 | |
| Preserve blacklisted certificates during the IPU 8 -> 9
 | |
| 
 | |
| Path for the blacklisted certificates has been changed on RHEL 9.
 | |
| The original paths on RHEL 8 and older systems have been:
 | |
|     /etc/pki/ca-trust/source/blacklist/
 | |
|     /usr/share/pki/ca-trust-source/blacklist/
 | |
| However on RHEL 9 the blacklist directory has been renamed to 'blocklist'.
 | |
| So the paths are:
 | |
|     /etc/pki/ca-trust/source/blocklist/
 | |
|     /usr/share/pki/ca-trust-source/blocklist/
 | |
| This actor moves all blacklisted certificates into the expected directories
 | |
| and fix symlinks if to point to the new dirs if they originally pointed
 | |
| to one of obsoleted dirs.
 | |
| 
 | |
| Covered cases:
 | |
| - covered situations with missing dirs
 | |
| - covered both mentioned blacklist directories
 | |
| - update symlinks in case they point to one of obsoleted directories
 | |
| - remove obsoleted directories when all files migrated successfully
 | |
| - execute /usr/bin/update-ca-trust in the end
 | |
| - remove original a blacklist directory in case all discovered files
 | |
|   inside are migrated successfully
 | |
| - print error logs in case of any issues so the upgrade does not
 | |
|   crash in case of troubles and users could deal with problems
 | |
|   manually after the upgrade
 | |
| 
 | |
| The actor is not covered by unit-tests as it's just a hotfix. Follow
 | |
| up works are expected to extend the problem with reports during
 | |
| preupgrade phases, improve the test coverage, ....
 | |
| 
 | |
| BZ: https://bugzilla.redhat.com/show_bug.cgi?id=2077432
 | |
| Followup ticket: CRYPTO-7097
 | |
| ---
 | |
|  .../actors/migrateblacklistca/actor.py        | 28 ++++++
 | |
|  .../libraries/migrateblacklistca.py           | 89 +++++++++++++++++++
 | |
|  .../tests/unit_test_migrateblacklistca.py     | 25 ++++++
 | |
|  3 files changed, 142 insertions(+)
 | |
|  create mode 100644 repos/system_upgrade/el8toel9/actors/migrateblacklistca/actor.py
 | |
|  create mode 100644 repos/system_upgrade/el8toel9/actors/migrateblacklistca/libraries/migrateblacklistca.py
 | |
|  create mode 100644 repos/system_upgrade/el8toel9/actors/migrateblacklistca/tests/unit_test_migrateblacklistca.py
 | |
| 
 | |
| diff --git a/repos/system_upgrade/el8toel9/actors/migrateblacklistca/actor.py b/repos/system_upgrade/el8toel9/actors/migrateblacklistca/actor.py
 | |
| new file mode 100644
 | |
| index 00000000..863a0063
 | |
| --- /dev/null
 | |
| +++ b/repos/system_upgrade/el8toel9/actors/migrateblacklistca/actor.py
 | |
| @@ -0,0 +1,28 @@
 | |
| +from leapp.actors import Actor
 | |
| +from leapp.libraries.actor import migrateblacklistca
 | |
| +from leapp.tags import ApplicationsPhaseTag, IPUWorkflowTag
 | |
| +
 | |
| +
 | |
| +class MigrateBlacklistCA(Actor):
 | |
| +    """
 | |
| +    Preserve blacklisted certificates during the upgrade
 | |
| +
 | |
| +    Path for the blacklisted certificates has been changed on RHEL 9.
 | |
| +    The original paths on RHEL 8 and older systems have been:
 | |
| +        /etc/pki/ca-trust/source/blacklist/
 | |
| +        /usr/share/pki/ca-trust-source/blacklist/
 | |
| +    However on RHEL 9 the blacklist directory has been renamed to 'blocklist'.
 | |
| +    So the new paths are:
 | |
| +        /etc/pki/ca-trust/source/blocklist/
 | |
| +        /usr/share/pki/ca-trust-source/blocklist/
 | |
| +    This actor moves all blacklisted certificates into the expected directories
 | |
| +    and fix symlinks if needed.
 | |
| +    """
 | |
| +
 | |
| +    name = 'migrate_blacklist_ca'
 | |
| +    consumes = ()
 | |
| +    produces = ()
 | |
| +    tags = (ApplicationsPhaseTag, IPUWorkflowTag)
 | |
| +
 | |
| +    def process(self):
 | |
| +        migrateblacklistca.process()
 | |
| diff --git a/repos/system_upgrade/el8toel9/actors/migrateblacklistca/libraries/migrateblacklistca.py b/repos/system_upgrade/el8toel9/actors/migrateblacklistca/libraries/migrateblacklistca.py
 | |
| new file mode 100644
 | |
| index 00000000..73c9d565
 | |
| --- /dev/null
 | |
| +++ b/repos/system_upgrade/el8toel9/actors/migrateblacklistca/libraries/migrateblacklistca.py
 | |
| @@ -0,0 +1,89 @@
 | |
| +import os
 | |
| +import shutil
 | |
| +
 | |
| +from leapp.libraries.stdlib import api, CalledProcessError, run
 | |
| +
 | |
| +# dict(orig_dir: new_dir)
 | |
| +DIRS_CHANGE = {
 | |
| +    '/etc/pki/ca-trust/source/blacklist/': '/etc/pki/ca-trust/source/blocklist/',
 | |
| +    '/usr/share/pki/ca-trust-source/blacklist/': '/usr/share/pki/ca-trust-source/blocklist/'
 | |
| +}
 | |
| +
 | |
| +
 | |
| +def _link_src_path(filepath):
 | |
| +    """
 | |
| +    Return expected target path for the symlink.
 | |
| +
 | |
| +    In case the symlink points to one of dirs supposed to be migrated in this
 | |
| +    actor, we need to point to the new directory instead.
 | |
| +
 | |
| +    In case the link points anywhere else, keep the target path as it is.
 | |
| +    """
 | |
| +    realpath = os.path.realpath(filepath)
 | |
| +    for dirname in DIRS_CHANGE:
 | |
| +        if realpath.startswith(dirname):
 | |
| +            return realpath.replace(dirname, DIRS_CHANGE[dirname])
 | |
| +
 | |
| +    # it seems we can keep this path
 | |
| +    return realpath
 | |
| +
 | |
| +
 | |
| +def _migrate_file(filename, src_basedir):
 | |
| +    dst_path = filename.replace(src_basedir, DIRS_CHANGE[src_basedir])
 | |
| +    if os.path.exists(dst_path):
 | |
| +        api.current_logger().info(
 | |
| +            'Skipping migration of the {} certificate. The target file already exists'
 | |
| +            .format(filename)
 | |
| +        )
 | |
| +        return
 | |
| +    os.makedirs(os.path.dirname(dst_path), mode=0o755, exist_ok=True)
 | |
| +    if os.path.islink(filename):
 | |
| +        # create the new symlink instead of the moving the file
 | |
| +        # as the target path could be different as well
 | |
| +        link_src_path = _link_src_path(filename)
 | |
| +        # TODO: is the broken symlink ok?
 | |
| +        os.symlink(link_src_path, dst_path)
 | |
| +        os.unlink(filename)
 | |
| +    else:
 | |
| +        # normal file, just move it
 | |
| +        shutil.move(filename, dst_path)
 | |
| +
 | |
| +
 | |
| +def _get_files(dirname):
 | |
| +    return run(['find', dirname, '-type', 'f,l'], split=True)['stdout']
 | |
| +
 | |
| +
 | |
| +def process():
 | |
| +    for dirname in DIRS_CHANGE:
 | |
| +        if not os.path.exists(dirname):
 | |
| +            # The directory does not exist; nothing to do here
 | |
| +            continue
 | |
| +        try:
 | |
| +            blacklisted_certs = _get_files(dirname)
 | |
| +        except (CalledProcessError, OSError) as e:
 | |
| +            # TODO: create post-upgrade report
 | |
| +            api.current_logger().error('Cannot get list of files in {}: {}.'.format(dirname, e))
 | |
| +            api.current_logger().error('Certificates under {} must be migrated manually.'.format(dirname))
 | |
| +            continue
 | |
| +        failed_files = []
 | |
| +        for filename in blacklisted_certs:
 | |
| +            try:
 | |
| +                _migrate_file(filename, dirname)
 | |
| +            except OSError as e:
 | |
| +                api.current_logger().error(
 | |
| +                    'Failed migration of blacklisted certificate {}: {}'
 | |
| +                    .format(filename, e)
 | |
| +                )
 | |
| +                failed_files.append(filename)
 | |
| +        if not failed_files:
 | |
| +            # the failed removal is not such a big issue here
 | |
| +            # clean the dir if all files have been migrated successfully
 | |
| +            shutil.rmtree(dirname, ignore_errors=True)
 | |
| +    try:
 | |
| +        run(['/usr/bin/update-ca-trust'])
 | |
| +    except (CalledProcessError, OSError) as e:
 | |
| +        api.current_logger().error(
 | |
| +            'Cannot update CA trust on the system.'
 | |
| +            ' It needs to be done manually after the in-place upgrade.'
 | |
| +            ' Reason: {}'.format(e)
 | |
| +        )
 | |
| diff --git a/repos/system_upgrade/el8toel9/actors/migrateblacklistca/tests/unit_test_migrateblacklistca.py b/repos/system_upgrade/el8toel9/actors/migrateblacklistca/tests/unit_test_migrateblacklistca.py
 | |
| new file mode 100644
 | |
| index 00000000..970dcb97
 | |
| --- /dev/null
 | |
| +++ b/repos/system_upgrade/el8toel9/actors/migrateblacklistca/tests/unit_test_migrateblacklistca.py
 | |
| @@ -0,0 +1,25 @@
 | |
| +import os
 | |
| +
 | |
| +from leapp.libraries.actor import migrateblacklistca
 | |
| +from leapp.libraries.common.testutils import CurrentActorMocked
 | |
| +from leapp.libraries.stdlib import api
 | |
| +
 | |
| +
 | |
| +class MockedGetFiles():
 | |
| +    def __init__(self):
 | |
| +        self.called = 0
 | |
| +
 | |
| +    def __call__(self):
 | |
| +        self.called += 1
 | |
| +        return []
 | |
| +
 | |
| +
 | |
| +def test_no_dirs_exist(monkeypatch):
 | |
| +    mocked_files = MockedGetFiles()
 | |
| +    monkeypatch.setattr(os.path, 'exists', lambda dummy: False)
 | |
| +    monkeypatch.setattr(migrateblacklistca, '_get_files', mocked_files)
 | |
| +    monkeypatch.setattr(api, 'current_actor', CurrentActorMocked())
 | |
| +    # this is bad mock, but we want to be sure that update-ca-trust is not
 | |
| +    # called on the testing machine
 | |
| +    monkeypatch.setattr(migrateblacklistca, 'run', lambda dummy: dummy)
 | |
| +    assert not mocked_files.called
 | |
| -- 
 | |
| 2.35.1
 | |
| 
 |