Beispiel #1
0
 def process(self):
     if has_package(InstalledRedHatSignedRPM, 'brltty'):
         report_with_remediation(
             title='Brltty has incompatible changes in the next major version',
             summary='The --message-delay brltty option has been renamed to --message-timeout.\n'
                     'The -U [--update-interval=] brltty option has been removed.',
             remediation='Please update your scripts to be compatible with the changes.',
             severity='low')
         (migrate_file, migrate_bt, migrate_espeak) = library.check_for_unsupported_cfg()
         report_summary = ''
         if migrate_bt:
             report_summary = 'Unsupported aliases for bluetooth devices (\'bth:\' and \'bluez:\') will be '
             report_summary += 'renamed to \'bluetooth:\'.'
         if migrate_espeak:
             if report_summary:
                 report_summary += '\n'
             report_summary += 'eSpeak speech driver is no longer supported, it will be switched to eSpeak-NG.'
         if report_summary:
             report_generic(
                 title='brltty configuration will be migrated',
                 summary=report_summary,
                 severity='low')
             self.produce(BrlttyMigrationDecision(migrate_file=migrate_file, migrate_bt=migrate_bt,
                                                  migrate_espeak=migrate_espeak))
         else:
             report_generic(
                 title='brltty configuration will be not migrated',
                 summary='brltty configuration seems to be compatible',
                 severity='low')
Beispiel #2
0
def check_ntp(installed_packages):
    service_data = [('ntpd', 'ntp', '/etc/ntp.conf'),
                    ('ntpdate', 'ntpdate', '/etc/ntp/step-tickers'),
                    ('ntp-wait', 'ntp-perl', None)]

    migrate_services = []
    migrate_configs = []
    for service, package, main_config in service_data:
        if package in installed_packages and \
                check_service('{}.service'.format(service)) and \
                (not main_config or is_file(main_config)):
            migrate_services.append(service)
            if main_config:
                migrate_configs.append(service)

    if migrate_configs:
        reporting.report_generic(
            title='{} configuration will be migrated'.format(
                ' and '.join(migrate_configs)),
            summary='{} service(s) detected to be enabled and active'.format(
                ', '.join(migrate_services)),
            severity='low')
        # Save configuration files that will be renamed in the upgrade
        config_tgz64 = get_tgz64([
            '/etc/ntp.conf', '/etc/ntp/keys', '/etc/ntp/crypto/pw',
            '/etc/ntp/step-tickers'
        ])
    else:
        api.current_logger().info(
            'ntpd/ntpdate configuration will not be migrated')
        migrate_services = []
        config_tgz64 = ''

    return NtpMigrationDecision(migrate_services=migrate_services,
                                config_tgz64=config_tgz64)
Beispiel #3
0
 def process(self):
     if platform.machine() != 'x86_64':
         report_generic(
             title='Unsupported arch',
             summary='Upgrade process is only supported on x86_64 systems.',
             severity='high',
             flags=['inhibitor'])
Beispiel #4
0
    def process(self):
        if not has_package(InstalledRedHatSignedRPM, 'sendmail'):
            return

        if config_applies_to_daemon(next(self.consume(TcpWrappersFacts)),
                                    'sendmail'):
            report_with_remediation(
                title='TCP wrappers support removed in the next major version',
                summary=
                'TCP wrappers are legacy host-based ACL (Access Control List) system '
                'which has been removed in the next major version of RHEL.',
                remediation=
                'Please migrate from TCP wrappers to some other access control mechanism and delete '
                'sendmail from the /etc/hosts.[allow|deny].',
                severity='high',
                flags=['inhibitor'])
            return
        migrate_files = library.check_files_for_compressed_ipv6()
        if migrate_files:
            report_generic(
                title='sendmail configuration will be migrated',
                summary=
                'IPv6 addresses will be uncompressed, check all IPv6 addresses in all sendmail '
                'configuration files for correctness.',
                severity='low')
            self.produce(
                SendmailMigrationDecision(migrate_files=migrate_files))
        else:
            self.log.info(
                'The sendmail configuration seems compatible - it won\'t be migrated.'
            )
Beispiel #5
0
def report_skipped_packages(message, packages):
    """Generate report message about skipped packages"""
    title = 'Packages will not be installed'
    summary = '{} {}\n{}'.format(len(packages), message,
                                 '\n'.join(['- ' + p for p in packages]))
    reporting.report_generic(title=title, summary=summary, severity='high')
    if is_verbose():
        api.show_message(summary)
Beispiel #6
0
def inhibit_upgrade(title):
    summary = 'Read documentation at: https://access.redhat.com/articles/3664871 for more information ' \
        'about how to retrieve the files'
    reporting.report_generic(title=title,
                             summary=summary,
                             severity='high',
                             flags=['inhibitor'])
    raise StopActorExecution()
Beispiel #7
0
def inhibit_upgrade(avail_bytes):
    additional_mib_needed = (MIN_AVAIL_BYTES_FOR_BOOT - avail_bytes) / 2**20
    # we use "reporting.report_generic" to allow mocking in the tests
    reporting.report_generic(
        title='Not enough space on /boot',
        summary='/boot needs additional {0} MiB to be able to accomodate the upgrade initramfs and new kernel.'.format(
            additional_mib_needed),
        severity='high',
        flags=['inhibitor'])
Beispiel #8
0
def inhibit(node_type):
    report_generic(
        title="Use of HA cluster detected. Upgrade can't proceed.",
        summary=("HA cluster is not supported by the inplace upgrade.\n"
                 "HA cluster configuration file(s) found."
                 " It seems to be a cluster {0}.".format(node_type)),
        severity="high",
        flags=["inhibitor"],
    )
Beispiel #9
0
def skip_check():
    """ Check if has environment variable to skip this actor checks """
    if os.getenv('LEAPP_SKIP_CHECK_SIGNED_PACKAGES'):
        reporting.report_generic(
            title='Skipped signed packages check',
            severity='low',
            summary=
            'Signed packages check skipped via LEAPP_SKIP_CHECK_SIGNED_PACKAGES env var'
        )
        raise StopActorExecution()
Beispiel #10
0
def skip_check():
    """ Check if an environment variable was used to skip this actor """
    if os.getenv('LEAPP_SKIP_CHECK_OS_RELEASE'):
        reporting.report_generic(
            title='Skipped OS release check',
            severity='high',
            summary=
            'Source RHEL release check skipped via LEAPP_SKIP_CHECK_OS_RELEASE env var.',
        )
        return True
    return False
Beispiel #11
0
 def process(self):
     for config in self.consume(OpenSshConfig):
         if config.use_privilege_separation is not None and \
            config.use_privilege_separation != "sandbox":
             report_generic(
                 title=
                 'OpenSSH configured not to use privilege separation sandbox',
                 summary='OpenSSH is configured to disable privilege '
                 'separation sandbox, which is decreasing security '
                 'and is no longer supported in RHEL 8',
                 severity='low')
Beispiel #12
0
 def process(self):
     for config in self.consume(OpenSshConfig):
         if not config.protocol:
             continue
         report_generic(
             title='OpenSSH configured with removed configuration Protocol',
             summary='OpenSSH is configured with removed configuration '
             'option Protocol. If this used to be for enabling '
             'SSHv1, this is no longer supported in RHEL 8. '
             'Otherwise this option can be simply removed.',
             severity='low')
Beispiel #13
0
    def process(self):
        error_detected = detect_config_error('/etc/default/grub')
        if error_detected:
            report_generic(
                title='Syntax error detected in grub configuration',
                summary=
                'Syntax error was detected in GRUB_CMDLINE_LINUX value of grub configuration. '
                'This error is causing booting and other issues. '
                'Error is automatically fixed by add_upgrade_boot_entry actor.',
                severity='low')

        self.produce(GrubConfigError(error_detected=error_detected))
Beispiel #14
0
def migrate_ntp(migrate_services, config_tgz64):
    # Map of ntp->chrony services and flag if using configuration
    service_map = {
        'ntpd': ('chronyd', True),
        'ntpdate': ('chronyd', True),
        'ntp-wait': ('chrony-wait', False)
    }

    # Minimal secure ntp.conf with no sources to migrate ntpdate only
    no_sources_directives = (
        '# This file was created to migrate ntpdate configuration to chrony\n'
        '# without ntp configuration (ntpd service was disabled)\n'
        'driftfile /var/lib/ntp/drift\n'
        'restrict default ignore nomodify notrap nopeer noquery\n')

    if not migrate_services:
        # Nothing to migrate
        return

    migrate_configs = []
    for service in migrate_services:
        if service not in service_map:
            raise StopActorExecutionError('Unknown service {}'.format(service))
        enable_service(service_map[service][0])
        if service_map[service][1]:
            migrate_configs.append(service)

    # Unpack archive with configuration files
    extract_tgz64(config_tgz64)

    if 'ntpd' in migrate_configs:
        ntp_conf = '/etc/ntp.conf'
    else:
        ntp_conf = '/etc/ntp.conf.nosources'
        write_file(ntp_conf, no_sources_directives)

    step_tickers = '/etc/ntp/step-tickers' if 'ntpdate' in migrate_configs else ''

    ignored_lines = ntp2chrony('/', ntp_conf, step_tickers)

    if not ignored_lines:
        reporting.report_generic(
            title='{} configuration migrated to chrony'.format(
                ' and '.join(migrate_configs)),
            summary='ntp2chrony executed successfully',
            severity='low')
    else:
        reporting.report_generic(
            title='{} configuration partially migrated to chrony'.format(
                ' and '.join(migrate_configs)),
            summary=
            'Some lines in /etc/ntp.conf were ignored in migration (check /etc/chrony.conf)',
            severity='medium')
Beispiel #15
0
    def process(self):
        decision = next(self.consume(SendmailMigrationDecision), None)
        if not decision or not decision.migrate_files:
            return

        for f in decision.migrate_files:
            library.migrate_file(f)
        list_separator_fmt = '\n    - '
        report_generic(title='sendmail configuration files migrated',
                       summary='Uncompressed IPv6 addresses in {}'.format(
                           list_separator_fmt.join(decision.migrate_files)),
                       severity='low')
Beispiel #16
0
    def process(self):
        interfaces = next(self.consume(PersistentNetNamesFacts)).interfaces

        if self.single_eth0(interfaces):
            self.disable_persistent_naming()
        elif self.ethX_count(interfaces) > 1:
            reporting.report_generic(
                title='Unsupported network configuration',
                summary='Detected multiple network interfaces using unstable kernel names (e.g. eth0, eth1). '
                        'Upgrade process can not continue because stability of names can not be guaranteed. '
                        'Please read the article at https://access.redhat.com/solutions/4067471 for more information.',
                severity='high',
                flags=['inhibitor'])
Beispiel #17
0
def scan_events(path):
    """ Scan JSON file containing PES events """
    try:
        events = parse_file(path)
    except (ValueError, KeyError):
        title = 'Missing/Invalid PES data file ({})'.format(path)
        summary = 'Read documentation at: https://access.redhat.com/articles/3664871 for more information ' \
            'about how to retrieve the files'
        reporting.report_generic(title=title,
                                 summary=summary,
                                 severity='high',
                                 flags=['inhibitor'])
        raise StopActorExecution()

    process_events(filter_events(events))
Beispiel #18
0
def check_chrony(chrony_installed):
    if not chrony_installed:
        api.current_logger().info('chrony package is not installed')
        return

    if is_config_default():
        reporting.report_generic(
            title='chrony using default configuration',
            summary=
            'default chrony configuration in RHEL8 uses leapsectz directive, which cannot be used with leap smearing NTP servers, and uses a single pool directive instead of four server directives',
            severity='medium')
    else:
        reporting.report_generic(
            title='chrony using non-default configuration',
            summary='chrony behavior will not change in RHEL8',
            severity='low')
Beispiel #19
0
    def process(self):

        # if NIS is not used for domainname resolution, everything is OK
        if not self.nis_in_nsswitch_hosts():
            return

        hostnames = self.hostnames_in_yp_conf()

        if len(hostnames) > 0:

            title = "Unsupported NIS configuration found"
            summary = "NIS may be used for domain name resolution only if NIS "\
                        "server is specified by IP. NIS servers specified by "\
                        "host name: {}".format(", ".join(hostnames))
            severity = "medium"
            report_generic(title=title, severity=severity, summary=summary)
Beispiel #20
0
    def process(self):
        service_name = 'leapp_resume.service'
        if os.path.isfile('/etc/systemd/system/{}'.format(service_name)):
            run(['systemctl', 'disable', service_name])
            try:
                os.unlink('/etc/systemd/system/{}'.format(service_name))
                os.unlink('/etc/systemd/system/default.target.wants/{}'.format(service_name))
            except OSError as e:
                if e.errno != errno.ENOENT:
                    raise

        report_generic(
            title='"{}" service deleted'.format(service_name),
            summary='"{}" was taking care of resuming upgrade process '
                    'after the first reboot.'.format(service_name),
            severity='low'
        )
Beispiel #21
0
    def process(self):
        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 config.protocol:
            report_generic(
                title='OpenSSH configured with removed configuration Protocol',
                summary='OpenSSH is configured with removed configuration '
                        'option Protocol. If this used to be for enabling '
                        'SSHv1, this is no longer supported in RHEL 8. '
                        'Otherwise this option can be simply removed.',
                severity='low')
Beispiel #22
0
 def process(self):
     for decision in self.consume(BrlttyMigrationDecision):
         report_summary = ''
         library.migrate_file(decision.migrate_file, decision.migrate_bt, decision.migrate_espeak)
         if decision.migrate_bt:
             report_summary = 'Unsupported aliases for bluetooth devices (\'bth:\' and \'bluez:\') was '
             report_summary += 'renamed to \'bluetooth:\' in {}'
             report_summary = report_summary.format(', '.join(decision.migrate_file))
         if decision.migrate_espeak:
             if report_summary:
                 report_summary += '\n'
             report_summary += 'eSpeak speech driver was switched to eSpeak-NG in {}'
             report_summary = report_summary.format(', '.join(decision.migrate_file))
         if decision.migrate_bt or decision.migrate_espeak:
             report_generic(
                 title='brltty configuration files migrated',
                 summary=report_summary,
                 severity='low')
Beispiel #23
0
def get_events(pes_events_filepath):
    """
    Get all the events from the source JSON file exported from PES.

    :return: List of Event tuples, where each event contains event type and input/output pkgs
    """
    try:
        return parse_pes_events_file(pes_events_filepath)
    except (ValueError, KeyError):
        title = 'Missing/Invalid PES data file ({})'.format(
            pes_events_filepath)
        summary = 'Read documentation at: https://access.redhat.com/articles/3664871 for more information ' \
            'about how to retrieve the files'
        reporting.report_generic(title=title,
                                 summary=summary,
                                 severity='high',
                                 flags=['inhibitor'])
        raise StopActorExecution()
Beispiel #24
0
    def process(self):
        model = next(self.consume(Authselect))
        decision = next(self.consume(AuthselectDecision))

        if not decision.confirmed or model.profile is None:
            return

        command = ['authselect', 'select', '--force', model.profile
                   ] + model.features

        try:
            run(command)
        except CalledProcessError as err:
            report_generic(title='Authselect call failed.', summary=str(err))
            return

        report_generic(title='System was converted to authselect.',
                       summary='System was converted to authselect with the '
                       'following call: "{}"'.format(' '.join(command)))
Beispiel #25
0
 def process(self):
     for fact in self.consume(InstalledRedHatSignedRPM):
         for rpm in fact.items:
             if rpm.name == 'postfix':
                 report_generic(
                     title=
                     'Postfix has incompatible changes in the next major version',
                     summary=
                     'Postfix 3.x has so called "compatibility safety net" that runs Postfix programs '
                     'with backwards-compatible default settings. It will log a warning whenever '
                     'backwards-compatible default setting may be required for continuity of service. '
                     'Based on this logging the system administrator can decide if any '
                     'backwards-compatible settings need to be made permanent in main.cf or master.cf, '
                     'before turning off the backwards-compatibility safety net.\n'
                     'The backward compatibility safety net is by default turned off in Red Hat '
                     'Enterprise Linux 8.\n'
                     'It can be turned on by running:  "postconf -e compatibility_level=0\n'
                     'It can be turned off by running: "postconf -e compatibility_level=2\n\n'
                     'In the Postfix MySQL database client, the default "option_group" value has changed '
                     'to "client", i.e. it now reads options from the [client] group from the MySQL '
                     'configuration file. To disable it, set "option_group" to the empty string.\n\n'
                     'The postqueue command no longer forces all message arrival times to be reported '
                     'in UTC. To get the old behavior, set TZ=UTC in main.cf:import_environment.\n\n'
                     'Postfix 3.2 enables elliptic curve negotiation. This changes the default '
                     'smtpd_tls_eecdh_grade setting to "auto", and introduces a new parameter '
                     '"tls_eecdh_auto_curves" with the names of curves that may be negotiated.\n\n'
                     'The "master.cf" chroot default value has changed from "y" (yes) to "n" (no). '
                     'This applies to master.cf services where chroot field is not explicitly '
                     'specified.\n\n'
                     'The "append_dot_mydomain" default value has changed from "yes" to "no". You may '
                     'need changing it to "yes" if senders cannot use complete domain names in e-mail '
                     'addresses.\n\n'
                     'The "relay_domains" default value has changed from "$mydestination" to the empty '
                     'value. This could result in unexpected "Relay access denied" errors or ETRN errors, '
                     'because now will postfix by default relay only for the localhost.\n\n'
                     'The "mynetworks_style" default value has changed from "subnet" to "host". '
                     'This parameter is used to implement the "permit_mynetworks" feature. The change '
                     'could result in unexpected "access denied" errors, because postfix will now by '
                     'default trust only the local machine, not the remote SMTP clients on the '
                     'same IP subnetwork.\n',
                     severity='low')
                 return
Beispiel #26
0
    def process(self):
        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 config.use_privilege_separation is not None and \
           config.use_privilege_separation != "sandbox":
            report_generic(
                title=
                'OpenSSH configured not to use privilege separation sandbox',
                summary='OpenSSH is configured to disable privilege '
                'separation sandbox, which is decreasing security '
                'and is no longer supported in RHEL 8',
                severity='low')
Beispiel #27
0
def check_memcached(memcached_installed):
    if not memcached_installed:
        api.current_logger().info('memcached package is not installed')
        return

    default_memcached_conf = is_sysconfig_default()
    disabled_udp_port = is_udp_disabled()

    if default_memcached_conf:
        reporting.report_generic(
            title='memcached service is using default configuration',
            summary=
            'memcached in RHEL8 listens on loopback only and has UDP port disabled by default',
            severity='medium')
    elif not disabled_udp_port:
        reporting.report_generic(
            title='memcached has enabled UDP port',
            summary=
            'memcached in RHEL7 has UDP port enabled by default, but it is disabled by default in RHEL8',
            severity='medium')
    else:
        reporting.report_generic(
            title='memcached has already disabled UDP port',
            summary='memcached in RHEL8 has UDP port disabled by default',
            severity='low')
Beispiel #28
0
def check_os_version(supported_version):
    """ Check OS version and inhibit upgrade if not the same as supported ones """
    if not isinstance(supported_version, dict):
        api.current_logger().warning('The supported version value is invalid.')
        raise StopActorExecution()

    facts_messages = api.consume(OSReleaseFacts)
    facts = next(facts_messages, None)
    if list(facts_messages):
        api.current_logger().warning(
            'Unexpectedly received more than one OSReleaseFacts message.')
    if not facts:
        raise StopActorExecutionError(
            'Could not check OS version',
            details={'details': 'No OSReleaseFacts facts found.'})

    if facts.release_id not in supported_version:
        reporting.report_generic(
            title='Unsupported OS',
            summary='Only RHEL is supported by the upgrade process',
            flags=['inhibitor'],
        )
        return

    if not isinstance(supported_version[facts.release_id], list):
        raise StopActorExecutionError(
            'Invalid versions',
            details={
                'details':
                'OS versions are invalid, please provide a valid list.'
            },
        )

    if facts.version_id not in supported_version[facts.release_id]:
        reporting.report_generic(
            title='Unsupported OS version',
            summary='The supported OS versions for the upgrade process: {}'.
            format(', '.join(supported_version[facts.release_id])),
            flags=['inhibitor'],
        )
Beispiel #29
0
def get_os_release_info(path):
    ''' Retrieve data about System OS release from provided file '''
    data = {}
    try:
        with open(path) as f:
            data = dict(l.strip().split('=', 1) for l in f.readlines()
                        if '=' in l)
    except IOError as e:
        reporting.report_generic(
            title='Error while collecting system OS facts',
            summary=str(e),
            severity='high',
            flags=['inhibitor'])
        return None

    return OSReleaseFacts(id=data.get('ID', '').strip('"'),
                          name=data.get('NAME', '').strip('"'),
                          pretty_name=data.get('PRETTY_NAME', '').strip('"'),
                          version=data.get('VERSION', '').strip('"'),
                          version_id=data.get('VERSION_ID', '').strip('"'),
                          variant=data.get('VARIANT', '').strip('"') or None,
                          variant_id=data.get('VARIANT_ID', '').strip('"')
                          or None)
Beispiel #30
0
 def process(self):
     for decision in self.consume(SelinuxRelabelDecision):
         if decision.set_relabel:
             try:
                 with open('/.autorelabel', 'w'):
                     pass
                 report_generic(
                     title='SElinux scheduled for relabelling',
                     summary=
                     '/.autorelabel file touched on root in order to schedule SElinux relabelling.',
                     severity='low',
                 )
             except OSError as e:
                 # FIXME: add an "action required" flag later
                 report_with_remediation(
                     title='Could not schedule SElinux for relabelling',
                     summary='./autorelabel file could not be created: {}.'.
                     format(e),
                     remediation=
                     'Please set autorelabelling manually after the upgrade.',
                     severity='high')
                 self.log.critical(
                     'Could not schedule SElinux for relabelling: %s.' % e)