Bug 2211985

Summary: Leapp inhibits the upgrade when openssh-server is not installed but /etc/ssh/sshd_config is present
Product: Red Hat Enterprise Linux 8 Reporter: jcastran
Component: leapp-repositoryAssignee: Petr Stodulka <pstodulk>
Status: NEW --- QA Contact: upgrades-and-conversions
Severity: medium Docs Contact:
Priority: medium    
Version: 8.8CC: mreznik
Target Milestone: rc   
Target Release: ---   
Hardware: Unspecified   
OS: Unspecified   
Whiteboard:
Fixed In Version: Doc Type: If docs needed, set a value
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:

Description jcastran 2023-06-02 20:47:39 UTC
Leapp outputs this inhibitor when the /etc/ssh/sshd_config file is unmodified.

Risk Factor: high (inhibitor)
Title: Possible problems with remote login using root account
Summary: OpenSSH configuration file will get updated to RHEL9 version, no longer allowing root login with password. It is a good practice to use non-root administrative user and non-password authentications, but if you rely on the remote root login, this change can lock you out of this system.
Remediation: [hint] If you depend on remote root logins using passwords, consider setting up a different user for remote administration or adding a comment into the sshd_config next to the "PermitRootLogin yes" directive to prevent rpm replacing it during the upgrade.

https://github.com/oamg/leapp-repository/blob/c3f32c8cd95011b9fb6b4c8a9ee27736ba551ae2/repos/system_upgrade/common/actors/opensshpermitrootlogincheck/actor.py#L114


The resolution for this inhibitor is to modify the sshd_config which prevents the new openssh-server package from replacing the old "unmodified" configuration file.


## The issue

If a customer does not have openssh-server installed, this inhibitor is impossible to resolve. 

The code first checks if the configuration is present. It then detects if the configuration is modified with rpm -V, but if the package is not present, this results in leapp considering the file unmodified.

~~~
# grep  def\ _read_rpm_modifications -A11 repositories/system_upgrade/common/libraries/rpms.py
def _read_rpm_modifications(config):
    """
    Ask RPM database whether the configuration file was modified.

    :param config: a config file to check
    """
    try:
        return stdlib.run(['rpm', '-Vf', config], split=True, checked=False)['stdout']
    except OSError as err:
        error = 'Failed to check the modification status of the file {}: {}'.format(config, str(err))
        stdlib.api.current_logger().error(error)
        return []
~~~


With the package removed we see that leapp reads the file and knows PermitRootLogin is false (which is a modified state) but shows at modified.

~~~
# yum remove openssh-server
# touch /etc/ssh/sshd_config
# leapp upgrade
# sqlite3 /var/lib/leapp/leapp.db "select message_data from messages_data where actor='read_openssh_config'" | jq '.'
{
"ciphers": null,
"deprecated_directives": [],
"macs": null,
"modified": false,
"permit_root_login": [],
"protocol": null,
"subsystem_sftp": null,
"use_privilege_separation": null
}
~~~

Leapp is inhibited unless the file is removed, or the openssh-server package is installed. 



## Expectations
Verify the openssh-server package is installed before performing any checks on openssh-server configuration files.



**Kyle Walker**
# diff /usr/share/leapp-repository/repositories/system_upgrade/common/actors/opensshpermitrootlogincheck/actor.py.orig /usr/share/leapp-repository/repositories/system_upgrade/common/actors/opensshpermitrootlogincheck/actor.py
5a6
> from leapp.libraries.common.rpms import has_package
7c8
< from leapp.models import OpenSshConfig, Report
---
> from leapp.models import OpenSshConfig, InstalledRedHatSignedRPM, Report
35c36
<   consumes = (OpenSshConfig, )
---
>   consumes = (OpenSshConfig, InstalledRedHatSignedRPM, )
40,55c41,59
<     openssh_messages = self.consume(OpenSshConfig)
<     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 get_source_major_version() == '7':
<       self.process7to8(config)
<     elif get_source_major_version() == '8':
<       self.process8to9(config)
<     else:
<       api.current_logger().warning('Unknown source major version: {} (expecting 7 or 8)'
<                     .format(get_source_major_version()))
---
>     has_server = has_package(InstalledRedHatSignedRPM, 'openssh-server')
>
>     if has_server:
>      openssh_messages = self.consume(OpenSshConfig)
>      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 get_source_major_version() == '7':
>        self.process7to8(config)
>      elif get_source_major_version() == '8':
>        self.process8to9(config)
>      else:
>        api.current_logger().warning('Unknown source major version: {} (expecting 7 or 8)'
>                      .format(get_source_major_version()))

Comment 1 Petr Stodulka 2023-06-06 12:32:06 UTC
Hi John, thanks for the report. Just curious, what is the reason for keeping the config file present on the system when openssh-server is not installed? Is customer using own "ssh server" app? Just want to understand the use case / customer story behind this scenario. Thanks

Comment 2 jcastran 2023-06-21 12:14:54 UTC
The configuration file should not have been kept on the system. We suspect the package was removed during the 6>7 upgrade incorrectly.

The file is present and the upgrade is completely blocked for false reasons. We can't continue the upgrade because the package is not marked as installed, so we can't verify the file has been modified. Leapp should verify the package is installed first, then check the files to avoid this issue.

Comment 3 Petr Stodulka 2023-06-21 12:24:11 UTC
I agree with you, just in this customer should not be really blocked as they can simply remove the file or install the missing package before the upgrade. But yes, I agree we should check it.