176 lines
6.9 KiB
Diff
176 lines
6.9 KiB
Diff
From ce1b83fafbbf3b323874fbb363e85a2e5abab4e2 Mon Sep 17 00:00:00 2001
|
|
From: Jakub Jelen <jjelen@redhat.com>
|
|
Date: Wed, 16 Mar 2022 21:48:04 +0100
|
|
Subject: [PATCH 14/39] Add actor for updating OpenSSH configuration to RHEL9
|
|
|
|
---
|
|
.../actors/opensshdropindirectory/actor.py | 29 ++++++++
|
|
.../libraries/opensshdropindirectory.py | 67 +++++++++++++++++++
|
|
.../test_opensshdropindirectory_prepend.py | 44 ++++++++++++
|
|
3 files changed, 140 insertions(+)
|
|
create mode 100644 repos/system_upgrade/el8toel9/actors/opensshdropindirectory/actor.py
|
|
create mode 100644 repos/system_upgrade/el8toel9/actors/opensshdropindirectory/libraries/opensshdropindirectory.py
|
|
create mode 100644 repos/system_upgrade/el8toel9/actors/opensshdropindirectory/tests/test_opensshdropindirectory_prepend.py
|
|
|
|
diff --git a/repos/system_upgrade/el8toel9/actors/opensshdropindirectory/actor.py b/repos/system_upgrade/el8toel9/actors/opensshdropindirectory/actor.py
|
|
new file mode 100644
|
|
index 00000000..17a0c01a
|
|
--- /dev/null
|
|
+++ b/repos/system_upgrade/el8toel9/actors/opensshdropindirectory/actor.py
|
|
@@ -0,0 +1,29 @@
|
|
+from leapp.actors import Actor
|
|
+from leapp.libraries.actor import opensshdropindirectory
|
|
+from leapp.models import InstalledRedHatSignedRPM, OpenSshConfig
|
|
+from leapp.tags import ApplicationsPhaseTag, IPUWorkflowTag
|
|
+
|
|
+
|
|
+class OpenSshDropInDirectory(Actor):
|
|
+ """
|
|
+ The RHEL 9 provides default configuration file with an Include directive.
|
|
+
|
|
+ If the configuration file was modified, it will not be replaced by the update
|
|
+ and we need to do couple of tweaks:
|
|
+
|
|
+ * Insert Include directive as expected by the rest of the OS
|
|
+ * Verify the resulting configuration is valid
|
|
+ * The only potentially problematic option is "Subsystem", but it is kept in the
|
|
+ main sshd_config even in RHEL9 so there is no obvious upgrade path where it
|
|
+ could cause issues (unlike the Debian version).
|
|
+
|
|
+ [1] https://bugzilla.mindrot.org/show_bug.cgi?id=3236
|
|
+ """
|
|
+
|
|
+ name = 'open_ssh_drop_in_directory'
|
|
+ consumes = (OpenSshConfig, InstalledRedHatSignedRPM,)
|
|
+ produces = ()
|
|
+ tags = (IPUWorkflowTag, ApplicationsPhaseTag,)
|
|
+
|
|
+ def process(self):
|
|
+ opensshdropindirectory.process(self.consume(OpenSshConfig))
|
|
diff --git a/repos/system_upgrade/el8toel9/actors/opensshdropindirectory/libraries/opensshdropindirectory.py b/repos/system_upgrade/el8toel9/actors/opensshdropindirectory/libraries/opensshdropindirectory.py
|
|
new file mode 100644
|
|
index 00000000..d55eee1c
|
|
--- /dev/null
|
|
+++ b/repos/system_upgrade/el8toel9/actors/opensshdropindirectory/libraries/opensshdropindirectory.py
|
|
@@ -0,0 +1,67 @@
|
|
+from leapp.exceptions import StopActorExecutionError
|
|
+from leapp.libraries.common.rpms import has_package
|
|
+from leapp.libraries.stdlib import api
|
|
+from leapp.models import InstalledRedHatSignedRPM
|
|
+
|
|
+# The main SSHD configuration file
|
|
+SSHD_CONFIG = '/etc/ssh/sshd_config'
|
|
+
|
|
+# The include directive needed, taken from RHEL9 sshd_config with leapp comment
|
|
+INCLUDE = 'Include /etc/ssh/sshd_config.d/*.conf'
|
|
+INCLUDE_BLOCK = ''.join(('# Added by leapp during upgrade from RHEL8 to RHEL9\n', INCLUDE, '\n'))
|
|
+
|
|
+
|
|
+def prepend_string_if_not_present(f, content, check_string):
|
|
+ """
|
|
+ This reads the open file descriptor and checks for presense of the `check_string`.
|
|
+ If not present, the `content` is prepended to the original content of the file and
|
|
+ result is written.
|
|
+ Note, that this requires opened file for both reading and writing, for example with:
|
|
+
|
|
+ with open(path, r+') as f:
|
|
+ """
|
|
+ lines = f.readlines()
|
|
+ for line in lines:
|
|
+ if line.lstrip().startswith(check_string):
|
|
+ # The directive is present
|
|
+ return
|
|
+
|
|
+ # prepend it otherwise, also with comment
|
|
+ f.seek(0)
|
|
+ f.write(''.join((content, ''.join(lines))))
|
|
+
|
|
+
|
|
+def process(openssh_messages):
|
|
+ """
|
|
+ The main logic of the actor:
|
|
+ * read the configuration file message
|
|
+ * skip if no action is needed
|
|
+ * package not installed
|
|
+ * the configuration file was not modified
|
|
+ * insert the include directive if it is not present yet
|
|
+ """
|
|
+ config = next(openssh_messages, None)
|
|
+ if list(openssh_messages):
|
|
+ api.current_logger().warning('Unexpectedly received more than one OpenSshConfig message.')
|
|
+ if not config:
|
|
+ raise StopActorExecutionError(
|
|
+ 'Could not check openssh configuration', details={'details': 'No OpenSshConfig facts found.'}
|
|
+ )
|
|
+
|
|
+ # If the package is not installed, there is no need to do anything
|
|
+ if not has_package(InstalledRedHatSignedRPM, 'openssh-server'):
|
|
+ return
|
|
+
|
|
+ # If the configuration file was not modified, the rpm update will bring the new
|
|
+ # changes by itself
|
|
+ if not config.modified:
|
|
+ return
|
|
+
|
|
+ # otherwise prepend the Include directive to the main sshd_config
|
|
+ api.current_logger().debug('Adding the Include directive to {}.'
|
|
+ .format(SSHD_CONFIG))
|
|
+ try:
|
|
+ with open(SSHD_CONFIG, 'r+') as f:
|
|
+ prepend_string_if_not_present(f, INCLUDE_BLOCK, INCLUDE)
|
|
+ except (OSError, IOError) as error:
|
|
+ api.current_logger().error('Failed to modify the file {}: {} '.format(SSHD_CONFIG, error))
|
|
diff --git a/repos/system_upgrade/el8toel9/actors/opensshdropindirectory/tests/test_opensshdropindirectory_prepend.py b/repos/system_upgrade/el8toel9/actors/opensshdropindirectory/tests/test_opensshdropindirectory_prepend.py
|
|
new file mode 100644
|
|
index 00000000..bccadf4b
|
|
--- /dev/null
|
|
+++ b/repos/system_upgrade/el8toel9/actors/opensshdropindirectory/tests/test_opensshdropindirectory_prepend.py
|
|
@@ -0,0 +1,44 @@
|
|
+import pytest
|
|
+
|
|
+from leapp.libraries.actor.opensshdropindirectory import prepend_string_if_not_present
|
|
+
|
|
+
|
|
+class MockFile(object):
|
|
+ def __init__(self, path, content=None):
|
|
+ self.path = path
|
|
+ self.content = content
|
|
+ self.error = False
|
|
+
|
|
+ def readlines(self):
|
|
+ return self.content.splitlines(True)
|
|
+
|
|
+ def seek(self, n):
|
|
+ self.content = ''
|
|
+
|
|
+ def write(self, content):
|
|
+ self.content = content
|
|
+
|
|
+
|
|
+testdata = (
|
|
+ ('', 'Prepend', 'Prepend',
|
|
+ 'Prepend'), # only prepend
|
|
+ ('Text', '', '',
|
|
+ 'Text'), # only text
|
|
+ ('Text', 'Prepend', 'Prepend',
|
|
+ 'PrependText'), # prepended text
|
|
+ ('Prepend\nText\n', 'Prepend', 'Prepend',
|
|
+ 'Prepend\nText\n'), # already present
|
|
+ ('Text\n', '# Comment\nPrepend\n', 'Prepend',
|
|
+ '# Comment\nPrepend\nText\n'), # different prepend than check string
|
|
+ ('Prepend\nText\n', '# Comment\nPrepend\n', 'Prepend',
|
|
+ 'Prepend\nText\n'), # different prepend than check string, already present
|
|
+)
|
|
+
|
|
+
|
|
+@pytest.mark.parametrize('file_content,prepend,check_string,expected', testdata)
|
|
+def test_prepend_string_if_not_present(file_content, prepend, check_string, expected):
|
|
+ f = MockFile('/etc/ssh/sshd_config', file_content)
|
|
+
|
|
+ prepend_string_if_not_present(f, prepend, check_string)
|
|
+
|
|
+ assert f.content == expected
|
|
--
|
|
2.35.3
|
|
|