def test_redact_exclude_no_regex(_process_content_redaction): ''' Verify that the _process_content_redaction call is made with exclude == list of strings and regex == False when a list of pattern strings is defined in rm_conf ''' conf = InsightsConfig() arch = InsightsArchive(conf) arch.create_archive_dir() # put something in the archive to redact test_file = os.path.join(arch.archive_dir, 'test.file') with open(test_file, 'w') as t: t.write(test_file_data) dc = DataCollector(conf, arch) rm_conf = {'patterns': ['1234', 'abcd']} if six.PY3: open_name = 'builtins.open' else: open_name = '__builtin__.open' with patch(open_name, create=True): dc.redact(rm_conf) _process_content_redaction.assert_called_once_with(test_file, ['1234', 'abcd'], False)
def test_redact_exclude_none(_process_content_redaction): ''' Verify that the _process_content_redaction call is made with exclude == None and regex == False when the patterns key is defined but value is an empty dict ''' conf = InsightsConfig() arch = InsightsArchive(conf) arch.create_archive_dir() # put something in the archive to redact test_file = os.path.join(arch.archive_dir, 'test.file') with open(test_file, 'w') as t: t.write(test_file_data) dc = DataCollector(conf, arch) rm_conf = {'patterns': {}} if six.PY3: open_name = 'builtins.open' else: open_name = '__builtin__.open' with patch(open_name, create=True): dc.redact(rm_conf) _process_content_redaction.assert_called_once_with(test_file, None, False)
def test_redact_call_process_redaction(_process_content_redaction): ''' Verify that redact() calls _process_content_redaction then writes the returned data back to the same file Also verifies that the "exclude" parameter is None and the "regex" parameter is False in the _process_content_redaction call when rm_conf is empty ''' conf = InsightsConfig() arch = InsightsArchive(conf) arch.create_archive_dir() # put something in the archive to redact test_file = os.path.join(arch.archive_dir, 'test.file') with open(test_file, 'w') as t: t.write(test_file_data) dc = DataCollector(conf, arch) rm_conf = {} if six.PY3: open_name = 'builtins.open' else: open_name = '__builtin__.open' with patch(open_name, create=True) as mock_open: dc.redact(rm_conf) _process_content_redaction.assert_called_once_with(test_file, None, False) mock_open.assert_called_once_with(test_file, 'wb') mock_open.return_value.__enter__.return_value.write.assert_called_once_with(_process_content_redaction.return_value)
def test_dont_archive_when_missing_dep(write_data_to_file): """ If missing dependencies do not archive it """ arch = InsightsArchive(InsightsConfig()) cmd = MagicMock(spec=InsightsCommand) cmd.get_output.return_value = "Missing Dependencies:" cmd.archive_path = '/path/to/command' arch.add_to_archive(cmd) write_data_to_file.assert_not_called()
def test_redact_call_walk(walk): ''' Verify that redact() calls os.walk and when an an archive structure is present in /var/tmp/**/insights-* ''' conf = InsightsConfig() arch = InsightsArchive(conf) arch.create_archive_dir() dc = DataCollector(conf, arch) rm_conf = {} dc.redact(rm_conf) walk.assert_called_once_with(arch.archive_dir)
def test_redact_call_walk_core(walk): ''' Verify that redact() calls os.walk and when an an archive structure is present in /var/tmp/**/insights-* With core collection, /data is added to the path ''' conf = InsightsConfig(core_collect=True) arch = InsightsArchive(conf) arch.create_archive_dir() dc = DataCollector(conf, arch) rm_conf = {} dc.redact(rm_conf) walk.assert_called_once_with(os.path.join(arch.archive_dir, 'data'))
def test_redact_bad_location(_process_content_redaction, walk): ''' Verify that redact() raises a RuntimeError if the directory present in InsightsArchive is in a location other than /var/tmp/**/insights-* ''' conf = InsightsConfig() arch = InsightsArchive(conf) for bad_path in ['/', '/home', '/etc', '/var/log/', '/home/test', '/var/tmp/f22D1d/ins2ghts']: arch.archive_dir = bad_path dc = DataCollector(conf, arch) rm_conf = {} with pytest.raises(RuntimeError): dc.redact(rm_conf) walk.assert_not_called() _process_content_redaction.assert_not_called()
def test_dont_archive_when_command_not_found(write_data_to_file): """ If the command is not found do not archive it """ arch = InsightsArchive(InsightsConfig()) arch.archive_dir = arch.create_archive_dir() arch.cmd_dir = arch.create_command_dir() cmd = MagicMock(spec=InsightsCommand) cmd.get_output.return_value = 'timeout: failed to run command blah: No such file or directory' cmd.archive_path = '/path/to/command' arch.add_to_archive(cmd) write_data_to_file.assert_not_called() cmd.get_output.return_value = '/usr/bin/command -a' arch.add_to_archive(cmd) write_data_to_file.assert_called_once()
def test_delete_archive_internal(): config = InsightsConfig(keep_archive=True) arch = InsightsArchive() _delete_archive_internal(config, arch) assert os.path.exists(arch.tmp_dir) assert os.path.exists(arch.archive_tmp_dir) config.keep_archive = False _delete_archive_internal(config, arch) assert not os.path.exists(arch.tmp_dir) assert not os.path.exists(arch.archive_tmp_dir)
def test_cleanup_tmp(): config = InsightsConfig(keep_archive=True) arch = InsightsArchive(config) arch.tar_file = os.path.join(arch.archive_tmp_dir, 'test.tar.gz') arch.cleanup_tmp() assert not os.path.exists(arch.tmp_dir) assert os.path.exists(arch.archive_tmp_dir) config.keep_archive = False arch.cleanup_tmp() assert not os.path.exists(arch.tmp_dir) assert not os.path.exists(arch.archive_tmp_dir)
class ComplianceClient: def __init__(self, config): self.config = config self.conn = InsightsConnection(config) self.archive = InsightsArchive(config) self._ssg_version = None def oscap_scan(self): self.inventory_id = self._get_inventory_id() self._assert_oscap_rpms_exist() initial_profiles = self.get_initial_profiles() matching_os_profiles = self.get_profiles_matching_os() profiles = self.profile_union_by_ref_id(matching_os_profiles, initial_profiles) if not profiles: logger.error( "System is not associated with any profiles. Assign profiles using the Compliance web UI.\n" ) exit(constants.sig_kill_bad) archive_dir = self.archive.create_archive_dir() results_need_repair = self.results_need_repair() for profile in profiles: tailoring_file = self.download_tailoring_file(profile) results_file = self._results_file(archive_dir, profile) self.run_scan(profile['attributes']['ref_id'], self.find_scap_policy( profile['attributes']['ref_id']), results_file, tailoring_file_path=tailoring_file) if results_need_repair: self.repair_results(results_file) if tailoring_file: os.remove(tailoring_file) return self.archive.create_tar_file(), COMPLIANCE_CONTENT_TYPE def download_tailoring_file(self, profile): if ('tailored' not in profile['attributes'] or profile['attributes']['tailored'] is False or ('os_minor_version' in profile['attributes'] and profile['attributes']['os_minor_version'] != self.os_minor_version())): return None # Download tailoring file to pass as argument to run_scan logger.debug( "Policy {0} is a tailored policy. Starting tailoring file download..." .format(profile['attributes']['ref_id'])) tailoring_file_path = tempfile.mkstemp( prefix='oscap_tailoring_file-{0}.'.format( profile['attributes']['ref_id']), suffix='.xml', dir='/var/tmp')[1] response = self.conn.session.get( "https://{0}/compliance/profiles/{1}/tailoring_file".format( self.config.base_url, profile['id'])) logger.debug("Response code: {0}".format(response.status_code)) if response.content is None: logger.info( "Problem downloading tailoring file for {0} to {1}".format( profile['attributes']['ref_id'], tailoring_file_path)) return None with open(tailoring_file_path, mode="w+b") as f: f.write(response.content) logger.info("Saved tailoring file for {0} to {1}".format( profile['attributes']['ref_id'], tailoring_file_path)) logger.debug("Policy {0} tailoring file download finished".format( profile['attributes']['ref_id'])) return tailoring_file_path def get_profiles(self, search): response = self.conn.session.get( "https://{0}/compliance/profiles".format(self.config.base_url), params={ 'search': search, 'relationships': 'false' }) logger.debug("Content of the response: {0} - {1}".format( response, response.json())) if response.status_code == 200: return (response.json().get('data') or []) else: return [] def get_initial_profiles(self): return self.get_profiles( 'system_ids={0} canonical=false external=false'.format( self.inventory_id)) def get_profiles_matching_os(self): return self.get_profiles( 'system_ids={0} canonical=false os_minor_version={1}'.format( self.inventory_id, self.os_minor_version())) def profile_union_by_ref_id(self, prioritized_profiles, merged_profiles): profiles = dict( (p['attributes']['ref_id'], p) for p in merged_profiles) profiles.update( dict((p['attributes']['ref_id'], p) for p in prioritized_profiles)) return list(profiles.values()) def os_release(self): _, version = os_release_info() return version def os_major_version(self): return findall("^[6-8]", self.os_release())[0] def os_minor_version(self): return findall("\d+$", self.os_release())[0] def profile_files(self): return glob("{0}*rhel{1}*.xml".format(POLICY_FILE_LOCATION, self.os_major_version())) def find_scap_policy(self, profile_ref_id): grepcmd = 'grep ' + profile_ref_id + ' ' + ' '.join( self.profile_files()) if not six.PY3: grepcmd = grepcmd.encode() rc, grep = call(grepcmd, keep_rc=True) if rc: logger.error( 'XML profile file not found matching ref_id {0}\n{1}\n'.format( profile_ref_id, grep)) return None filenames = findall('/usr/share/xml/scap/.+xml', grep) if not filenames: logger.error( 'No XML profile files found matching ref_id {0}\n{1}\n'.format( profile_ref_id, ' '.join(filenames))) exit(constants.sig_kill_bad) return filenames[0] def build_oscap_command(self, profile_ref_id, policy_xml, output_path, tailoring_file_path): command = 'oscap xccdf eval --profile ' + profile_ref_id if tailoring_file_path: command += ' --tailoring-file ' + tailoring_file_path command += ' --results ' + output_path + ' ' + policy_xml return command def run_scan(self, profile_ref_id, policy_xml, output_path, tailoring_file_path=None): if policy_xml is None: return logger.info('Running scan for {0}... this may take a while'.format( profile_ref_id)) env = os.environ.copy() env.update({'TZ': 'UTC'}) oscap_command = self.build_oscap_command(profile_ref_id, policy_xml, output_path, tailoring_file_path) if not six.PY3: oscap_command = oscap_command.encode() rc, oscap = call(oscap_command, keep_rc=True, env=env) if rc and rc != NONCOMPLIANT_STATUS: logger.error('Scan failed') logger.error(oscap) exit(constants.sig_kill_bad) @property def ssg_version(self): if not self._ssg_version: self._ssg_version = self.get_ssg_version() return self._ssg_version def get_ssg_version(self): rpmcmd = 'rpm -qa --qf "%{VERSION}" ' + SSG_PACKAGE if not six.PY3: rpmcmd = rpmcmd.encode() rc, ssg_version = call(rpmcmd, keep_rc=True) if rc: logger.warning( 'Tried determinig SSG version but failed: {0}.\n'.format( ssg_version)) return logger.info('System uses SSG version %s', ssg_version) return ssg_version def results_need_repair(self): return self.ssg_version in VERSIONS_FOR_REPAIR def repair_results(self, results_file): if not os.path.isfile(results_file): return if not self.ssg_version: logger.warning("Couldn't repair SSG version in results file %s", results_file) return results_file_in = '{0}.in'.format(results_file) os.rename(results_file, results_file_in) with open(results_file_in, 'r') as in_file: with open(results_file, 'w') as out_file: is_repaired = self._repair_ssg_version_in_results( in_file, out_file, self.ssg_version) os.remove(results_file_in) if is_repaired: logger.debug('Repaired version in results file %s', results_file) return is_repaired def _repair_ssg_version_in_results(self, in_file, out_file, ssg_version): replacement = '<version>{0}</version>'.format(ssg_version) is_repaired = False for line in in_file: if is_repaired or SNIPPET_TO_FIX not in line: out_file.write(line) else: out_file.write(line.replace(SNIPPET_TO_FIX, replacement)) is_repaired = True logger.debug('Substituted "%s" with "%s" in %s', SNIPPET_TO_FIX, replacement, out_file.name) return is_repaired def _assert_oscap_rpms_exist(self): rpmcmd = 'rpm -qa ' + ' '.join(REQUIRED_PACKAGES) if not six.PY3: rpmcmd = rpmcmd.encode() rc, rpm = call(rpmcmd, keep_rc=True) if rc: logger.error( 'Tried running rpm -qa but failed: {0}.\n'.format(rpm)) exit(constants.sig_kill_bad) else: if len(rpm.strip().split('\n')) < len(REQUIRED_PACKAGES): logger.error( 'Missing required packages for compliance scanning. Please ensure the following packages are installed: {0}\n' .format(', '.join(REQUIRED_PACKAGES))) exit(constants.sig_kill_bad) def _get_inventory_id(self): systems = self.conn._fetch_system_by_machine_id() if len(systems) == 1 and 'id' in systems[0]: return systems[0].get('id') else: logger.error('Failed to find system in Inventory') exit(constants.sig_kill_bad) def _results_file(self, archive_dir, profile): return os.path.join( archive_dir, 'oscap_results-{0}.xml'.format(profile['attributes']['ref_id']))
def __init__(self, config): self.config = config self.conn = InsightsConnection(config) self.hostname = determine_hostname() self.archive = InsightsArchive(config)
class ComplianceClient: def __init__(self, config): self.config = config self.conn = InsightsConnection(config) self.hostname = determine_hostname() self.archive = InsightsArchive(config) def oscap_scan(self): self._assert_oscap_rpms_exist() policies = self.get_policies() if not policies: logger.error("System is not associated with any profiles. Assign profiles by either uploading a SCAP scan or using the compliance web UI.\n") exit(constants.sig_kill_bad) for policy in policies: self.run_scan( policy['attributes']['ref_id'], self.find_scap_policy(policy['attributes']['ref_id']), '/var/tmp/oscap_results-{0}.xml'.format(policy['attributes']['ref_id']), tailoring_file_path=self.download_tailoring_file(policy) ) return self.archive.create_tar_file(), COMPLIANCE_CONTENT_TYPE def download_tailoring_file(self, policy): if 'tailored' not in policy['attributes'] or policy['attributes']['tailored'] is False: return None # Download tailoring file to pass as argument to run_scan logger.debug( "Policy {0} is a tailored policy. Starting tailoring file download...".format(policy['attributes']['ref_id']) ) tailoring_file_path = "/var/tmp/oscap_tailoring_file-{0}.xml".format(policy['attributes']['ref_id']) response = self.conn.session.get( "https://{0}/compliance/profiles/{1}/tailoring_file".format(self.config.base_url, policy['id']) ) logger.debug("Response code: {0}".format(response.status_code)) if response.content is None: logger.info("Problem downloading tailoring file for {0} to {1}".format(policy['attributes']['ref_id'], tailoring_file_path)) return None with open(tailoring_file_path, mode="w+b") as f: f.write(response.content) logger.info("Saved tailoring file for {0} to {1}".format(policy['attributes']['ref_id'], tailoring_file_path)) logger.debug("Policy {0} tailoring file download finished".format(policy['attributes']['ref_id'])) return tailoring_file_path # TODO: Not a typo! This endpoint gives OSCAP policies, not profiles # We need to update compliance-backend to fix this def get_policies(self): response = self.conn.session.get("https://{0}/compliance/profiles".format(self.config.base_url), params={'search': 'system_names={0}'.format(self.hostname)}) logger.debug("Content of the response: {0} - {1}".format(response, response.json())) if response.status_code == 200: return (response.json().get('data') or []) else: return [] def os_release(self): _, version, _ = linux_distribution() return findall("^[6-8]", version)[0] def profile_files(self): return glob("{0}*rhel{1}*.xml".format(POLICY_FILE_LOCATION, self.os_release())) def find_scap_policy(self, profile_ref_id): rc, grep = call(('grep ' + profile_ref_id + ' ' + ' '.join(self.profile_files())).encode(), keep_rc=True) if rc: logger.error('XML profile file not found matching ref_id {0}\n{1}\n'.format(profile_ref_id, grep)) exit(constants.sig_kill_bad) filenames = findall('/usr/share/xml/scap/.+xml', grep) if not filenames: logger.error('No XML profile files found matching ref_id {0}\n{1}\n'.format(profile_ref_id, ' '.join(filenames))) exit(constants.sig_kill_bad) return filenames[0] def build_oscap_command(self, profile_ref_id, policy_xml, output_path, tailoring_file_path): command = 'oscap xccdf eval --profile ' + profile_ref_id if tailoring_file_path: command += ' --tailoring-file ' + tailoring_file_path command += ' --results ' + output_path + ' ' + policy_xml return command def run_scan(self, profile_ref_id, policy_xml, output_path, tailoring_file_path=None): logger.info('Running scan for {0}... this may take a while'.format(profile_ref_id)) env = os.environ.copy() env.update({'TZ': 'UTC'}) oscap_command = self.build_oscap_command(profile_ref_id, policy_xml, output_path, tailoring_file_path) rc, oscap = call(oscap_command.encode(), keep_rc=True, env=env) if rc and rc != NONCOMPLIANT_STATUS: logger.error('Scan failed') logger.error(oscap) exit(constants.sig_kill_bad) else: self.archive.copy_file(output_path) def _assert_oscap_rpms_exist(self): rc, rpm = call('rpm -qa ' + ' '.join(REQUIRED_PACKAGES), keep_rc=True) if rc: logger.error('Tried running rpm -qa but failed: {0}.\n'.format(rpm)) exit(constants.sig_kill_bad) else: if len(rpm.strip().split('\n')) < len(REQUIRED_PACKAGES): logger.error('Missing required packages for compliance scanning. Please ensure the following packages are installed: {0}\n'.format(', '.join(REQUIRED_PACKAGES))) exit(constants.sig_kill_bad)
def __init__(self, config): self.config = config self.conn = InsightsConnection(config) self.hostname = get_canonical_facts().get('fqdn', '') self.archive = InsightsArchive(config)
class ComplianceClient: def __init__(self, config): self.config = config self.conn = InsightsConnection(config) self.hostname = get_canonical_facts().get('fqdn', '') self.archive = InsightsArchive(config) def oscap_scan(self): self._assert_oscap_rpms_exist() policies = self.get_policies() if not policies: logger.error( "System is not associated with any profiles. Assign profiles by either uploading a SCAP scan or using the compliance web UI.\n" ) exit(constants.sig_kill_bad) profile_ref_ids = [policy['ref_id'] for policy in policies] for profile_ref_id in profile_ref_ids: self.run_scan( profile_ref_id, self.find_scap_policy(profile_ref_id), '/var/tmp/oscap_results-{0}.xml'.format(profile_ref_id)) return self.archive.create_tar_file(), COMPLIANCE_CONTENT_TYPE # TODO: Not a typo! This endpoint gives OSCAP policies, not profiles # We need to update compliance-backend to fix this def get_policies(self): response = self.conn.session.get( "https://{0}/compliance/systems".format(self.config.base_url), params={'search': 'name={0}'.format(self.hostname)}) if response.status_code == 200: return (response.json().get('data') or [{}])[0].get('attributes', {}).get('profiles', []) else: return [] def os_release(self): _, version, _ = linux_distribution() return findall("^[6-8]", version)[0] def profile_files(self): return glob("{0}*rhel{1}*.xml".format(POLICY_FILE_LOCATION, self.os_release())) def find_scap_policy(self, profile_ref_id): rc, grep = call('grep ' + profile_ref_id + ' ' + ' '.join(self.profile_files()), keep_rc=True) if rc: logger.error( 'XML profile file not found matching ref_id {0}\n{1}\n'.format( profile_ref_id, grep)) exit(constants.sig_kill_bad) filenames = findall('/usr/share/xml/scap/.+xml', grep) if not filenames: logger.error( 'No XML profile files found matching ref_id {0}\n{1}\n'.format( profile_ref_id, ' '.join(filenames))) exit(constants.sig_kill_bad) return filenames[0] def run_scan(self, profile_ref_id, policy_xml, output_path): logger.info('Running scan for {0}... this may take a while'.format( profile_ref_id)) env = os.environ.copy() env.update({'TZ': 'UTC'}) rc, oscap = call('oscap xccdf eval --profile ' + profile_ref_id + ' --results ' + output_path + ' ' + policy_xml, keep_rc=True, env=env) if rc and rc != NONCOMPLIANT_STATUS: logger.error('Scan failed') logger.error(oscap) exit(constants.sig_kill_bad) else: self.archive.copy_file(output_path) def _assert_oscap_rpms_exist(self): rc, rpm = call('rpm -qa ' + ' '.join(REQUIRED_PACKAGES), keep_rc=True) if rc: logger.error( 'Tried running rpm -qa but failed: {0}.\n'.format(rpm)) exit(constants.sig_kill_bad) else: if len(rpm.strip().split('\n')) < len(REQUIRED_PACKAGES): logger.error( 'Missing required packages for compliance scanning. Please ensure the following packages are installed: {0}\n' .format(', '.join(REQUIRED_PACKAGES))) exit(constants.sig_kill_bad)
class ComplianceClient: def __init__(self, config): self.config = config self.conn = InsightsConnection(config) self.archive = InsightsArchive(config) def oscap_scan(self): self.inventory_id = self._get_inventory_id() self._assert_oscap_rpms_exist() initial_profiles = self.get_initial_profiles() matching_os_profiles = self.get_profiles_matching_os() profiles = self.profile_union_by_ref_id(matching_os_profiles, initial_profiles) if not profiles: logger.error( "System is not associated with any profiles. Assign profiles using the Compliance web UI.\n" ) exit(constants.sig_kill_bad) for profile in profiles: self.run_scan( profile['attributes']['ref_id'], self.find_scap_policy(profile['attributes']['ref_id']), '/var/tmp/oscap_results-{0}.xml'.format( profile['attributes']['ref_id']), tailoring_file_path=self.download_tailoring_file(profile)) return self.archive.create_tar_file(), COMPLIANCE_CONTENT_TYPE def download_tailoring_file(self, profile): if ('tailored' not in profile['attributes'] or profile['attributes']['tailored'] is False or ('os_minor_version' in profile['attributes'] and profile['attributes']['os_minor_version'] != self.os_minor_version())): return None # Download tailoring file to pass as argument to run_scan logger.debug( "Policy {0} is a tailored policy. Starting tailoring file download..." .format(profile['attributes']['ref_id'])) tailoring_file_path = "/var/tmp/oscap_tailoring_file-{0}.xml".format( profile['attributes']['ref_id']) response = self.conn.session.get( "https://{0}/compliance/profiles/{1}/tailoring_file".format( self.config.base_url, profile['id'])) logger.debug("Response code: {0}".format(response.status_code)) if response.content is None: logger.info( "Problem downloading tailoring file for {0} to {1}".format( profile['attributes']['ref_id'], tailoring_file_path)) return None with open(tailoring_file_path, mode="w+b") as f: f.write(response.content) logger.info("Saved tailoring file for {0} to {1}".format( profile['attributes']['ref_id'], tailoring_file_path)) logger.debug("Policy {0} tailoring file download finished".format( profile['attributes']['ref_id'])) return tailoring_file_path def get_profiles(self, search): response = self.conn.session.get( "https://{0}/compliance/profiles".format(self.config.base_url), params={'search': search}) logger.debug("Content of the response: {0} - {1}".format( response, response.json())) if response.status_code == 200: return (response.json().get('data') or []) else: return [] def get_initial_profiles(self): return self.get_profiles( 'system_ids={0} canonical=false external=false'.format( self.inventory_id)) def get_profiles_matching_os(self): return self.get_profiles( 'system_ids={0} canonical=false os_minor_version={1}'.format( self.inventory_id, self.os_minor_version())) def profile_union_by_ref_id(self, prioritized_profiles, merged_profiles): profiles = dict( (p['attributes']['ref_id'], p) for p in merged_profiles) profiles.update( dict((p['attributes']['ref_id'], p) for p in prioritized_profiles)) return list(profiles.values()) def os_release(self): _, version = os_release_info() return version def os_major_version(self): return findall("^[6-8]", self.os_release())[0] def os_minor_version(self): return findall("\d+$", self.os_release())[0] def profile_files(self): return glob("{0}*rhel{1}*.xml".format(POLICY_FILE_LOCATION, self.os_major_version())) def find_scap_policy(self, profile_ref_id): grepcmd = 'grep ' + profile_ref_id + ' ' + ' '.join( self.profile_files()) if not six.PY3: grepcmd = grepcmd.encode() rc, grep = call(grepcmd, keep_rc=True) if rc: logger.error( 'XML profile file not found matching ref_id {0}\n{1}\n'.format( profile_ref_id, grep)) return None filenames = findall('/usr/share/xml/scap/.+xml', grep) if not filenames: logger.error( 'No XML profile files found matching ref_id {0}\n{1}\n'.format( profile_ref_id, ' '.join(filenames))) exit(constants.sig_kill_bad) return filenames[0] def build_oscap_command(self, profile_ref_id, policy_xml, output_path, tailoring_file_path): command = 'oscap xccdf eval --profile ' + profile_ref_id if tailoring_file_path: command += ' --tailoring-file ' + tailoring_file_path command += ' --results ' + output_path + ' ' + policy_xml return command def run_scan(self, profile_ref_id, policy_xml, output_path, tailoring_file_path=None): if policy_xml is None: return logger.info('Running scan for {0}... this may take a while'.format( profile_ref_id)) env = os.environ.copy() env.update({'TZ': 'UTC'}) oscap_command = self.build_oscap_command(profile_ref_id, policy_xml, output_path, tailoring_file_path) if not six.PY3: oscap_command = oscap_command.encode() rc, oscap = call(oscap_command, keep_rc=True, env=env) if rc and rc != NONCOMPLIANT_STATUS: logger.error('Scan failed') logger.error(oscap) exit(constants.sig_kill_bad) else: self.archive.copy_file(output_path) def _assert_oscap_rpms_exist(self): rpmcmd = 'rpm -qa ' + ' '.join(REQUIRED_PACKAGES) if not six.PY3: rpmcmd = rpmcmd.encode() rc, rpm = call(rpmcmd, keep_rc=True) if rc: logger.error( 'Tried running rpm -qa but failed: {0}.\n'.format(rpm)) exit(constants.sig_kill_bad) else: if len(rpm.strip().split('\n')) < len(REQUIRED_PACKAGES): logger.error( 'Missing required packages for compliance scanning. Please ensure the following packages are installed: {0}\n' .format(', '.join(REQUIRED_PACKAGES))) exit(constants.sig_kill_bad) def _get_inventory_id(self): systems = self.conn._fetch_system_by_machine_id() if len(systems) == 1 and 'id' in systems[0]: return systems[0].get('id') else: logger.error('Failed to find system in Inventory') exit(constants.sig_kill_bad)
def __init__(self, config): self.config = config self.conn = InsightsConnection(config) self.archive = InsightsArchive(config)