diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 733b1f6925..1f0f127fc9 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -1753,10 +1753,9 @@ class HostSerializer(BaseSerializerWithVariables): 'has_inventory_sources', 'last_job', 'last_job_host_summary', - 'insights_system_id', 'ansible_facts_modified', ) - read_only_fields = ('last_job', 'last_job_host_summary', 'insights_system_id', 'ansible_facts_modified') + read_only_fields = ('last_job', 'last_job_host_summary', 'ansible_facts_modified') def build_relational_field(self, field_name, relation_info): field_class, field_kwargs = super(HostSerializer, self).build_relational_field(field_name, relation_info) @@ -1780,7 +1779,6 @@ class HostSerializer(BaseSerializerWithVariables): smart_inventories=self.reverse('api:host_smart_inventories_list', kwargs={'pk': obj.pk}), ad_hoc_commands=self.reverse('api:host_ad_hoc_commands_list', kwargs={'pk': obj.pk}), ad_hoc_command_events=self.reverse('api:host_ad_hoc_command_events_list', kwargs={'pk': obj.pk}), - insights=self.reverse('api:host_insights', kwargs={'pk': obj.pk}), ansible_facts=self.reverse('api:host_ansible_facts_detail', kwargs={'pk': obj.pk}), ) ) diff --git a/awx/api/urls/host.py b/awx/api/urls/host.py index c41baff940..d06608bf86 100644 --- a/awx/api/urls/host.py +++ b/awx/api/urls/host.py @@ -16,7 +16,6 @@ from awx.api.views import ( HostSmartInventoriesList, HostAdHocCommandsList, HostAdHocCommandEventsList, - HostInsights, ) @@ -33,7 +32,6 @@ urls = [ url(r'^(?P[0-9]+)/smart_inventories/$', HostSmartInventoriesList.as_view(), name='host_smart_inventories_list'), url(r'^(?P[0-9]+)/ad_hoc_commands/$', HostAdHocCommandsList.as_view(), name='host_ad_hoc_commands_list'), url(r'^(?P[0-9]+)/ad_hoc_command_events/$', HostAdHocCommandEventsList.as_view(), name='host_ad_hoc_command_events_list'), - url(r'^(?P[0-9]+)/insights/$', HostInsights.as_view(), name='host_insights'), ] __all__ = ['urls'] diff --git a/awx/api/views/__init__.py b/awx/api/views/__init__.py index 8d34dcb8a9..d753f9cb98 100644 --- a/awx/api/views/__init__.py +++ b/awx/api/views/__init__.py @@ -90,17 +90,14 @@ from awx.main import models from awx.main.utils import ( camelcase_to_underscore, extract_ansible_vars, - get_awx_http_client_headers, get_object_or_400, getattrd, get_pk_from_dict, schedule_task_manager, ignore_inventory_computed_fields, - set_environ, ) from awx.main.utils.encryption import encrypt_value from awx.main.utils.filters import SmartFilter -from awx.main.utils.insights import filter_insights_api_response from awx.main.redact import UriCleaner from awx.api.permissions import ( JobTemplateCallbackPermission, @@ -1700,106 +1697,6 @@ class GatewayTimeout(APIException): default_code = 'gateway_timeout' -class HostInsights(GenericAPIView): - - model = models.Host - serializer_class = serializers.EmptySerializer - - def _call_insights_api(self, url, session, headers): - try: - with set_environ(**settings.AWX_TASK_ENV): - res = session.get(url, headers=headers, timeout=120) - except requests.exceptions.SSLError: - raise BadGateway(_('SSLError while trying to connect to {}').format(url)) - except requests.exceptions.Timeout: - raise GatewayTimeout(_('Request to {} timed out.').format(url)) - except requests.exceptions.RequestException as e: - raise BadGateway(_('Unknown exception {} while trying to GET {}').format(e, url)) - - if res.status_code == 401: - raise BadGateway(_('Unauthorized access. Please check your Insights Credential username and password.')) - elif res.status_code != 200: - raise BadGateway( - _('Failed to access the Insights API at URL {}.' ' Server responded with {} status code and message {}').format( - url, res.status_code, res.content - ) - ) - - try: - return res.json() - except ValueError: - raise BadGateway(_('Expected JSON response from Insights at URL {}' ' but instead got {}').format(url, res.content)) - - def _get_session(self, username, password): - session = requests.Session() - session.auth = requests.auth.HTTPBasicAuth(username, password) - - return session - - def _get_platform_info(self, host, session, headers): - url = '{}/api/inventory/v1/hosts?insights_id={}'.format(settings.INSIGHTS_URL_BASE, host.insights_system_id) - res = self._call_insights_api(url, session, headers) - try: - res['results'][0]['id'] - except (IndexError, KeyError): - raise NotFound(_('Could not translate Insights system ID {}' ' into an Insights platform ID.').format(host.insights_system_id)) - - return res['results'][0] - - def _get_reports(self, platform_id, session, headers): - url = '{}/api/insights/v1/system/{}/reports/'.format(settings.INSIGHTS_URL_BASE, platform_id) - - return self._call_insights_api(url, session, headers) - - def _get_remediations(self, platform_id, session, headers): - url = '{}/api/remediations/v1/remediations?system={}'.format(settings.INSIGHTS_URL_BASE, platform_id) - - remediations = [] - - # Iterate over all of the pages of content. - while url: - data = self._call_insights_api(url, session, headers) - remediations.extend(data['data']) - - url = data['links']['next'] # Will be `None` if this is the last page. - - return remediations - - def _get_insights(self, host, session, headers): - platform_info = self._get_platform_info(host, session, headers) - platform_id = platform_info['id'] - reports = self._get_reports(platform_id, session, headers) - remediations = self._get_remediations(platform_id, session, headers) - - return {'insights_content': filter_insights_api_response(platform_info, reports, remediations)} - - def get(self, request, *args, **kwargs): - host = self.get_object() - cred = None - - if host.insights_system_id is None: - return Response(dict(error=_('This host is not recognized as an Insights host.')), status=status.HTTP_404_NOT_FOUND) - - if host.inventory and host.inventory.insights_credential: - cred = host.inventory.insights_credential - else: - return Response(dict(error=_('The Insights Credential for "{}" was not found.').format(host.inventory.name)), status=status.HTTP_404_NOT_FOUND) - - username = cred.get_input('username', default='') - password = cred.get_input('password', default='') - session = self._get_session(username, password) - headers = get_awx_http_client_headers() - - data = self._get_insights(host, session, headers) - return Response(data, status=status.HTTP_200_OK) - - def handle_exception(self, exc): - # Continue supporting the slightly different way we have handled error responses on this view. - response = super().handle_exception(exc) - response.data['error'] = response.data.pop('detail') - return response - - class GroupList(ListCreateAPIView): model = models.Group diff --git a/awx/main/constants.py b/awx/main/constants.py index fbd783b968..e8ac403723 100644 --- a/awx/main/constants.py +++ b/awx/main/constants.py @@ -14,7 +14,7 @@ __all__ = [ 'STANDARD_INVENTORY_UPDATE_ENV', ] -CLOUD_PROVIDERS = ('azure_rm', 'ec2', 'gce', 'vmware', 'openstack', 'rhv', 'satellite6', 'tower') +CLOUD_PROVIDERS = ('azure_rm', 'ec2', 'gce', 'vmware', 'openstack', 'rhv', 'satellite6', 'tower', 'insights') PRIVILEGE_ESCALATION_METHODS = [ ('sudo', _('Sudo')), ('su', _('Su')), diff --git a/awx/main/migrations/0146_add_insights_inventory.py b/awx/main/migrations/0146_add_insights_inventory.py new file mode 100644 index 0000000000..d4d9df3c32 --- /dev/null +++ b/awx/main/migrations/0146_add_insights_inventory.py @@ -0,0 +1,59 @@ +# Generated by Django 2.2.16 on 2021-06-08 18:59 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0145_deregister_managed_ee_objs'), + ] + + operations = [ + migrations.RemoveField( + model_name='host', + name='insights_system_id', + ), + migrations.AlterField( + model_name='inventorysource', + name='source', + field=models.CharField( + choices=[ + ('file', 'File, Directory or Script'), + ('scm', 'Sourced from a Project'), + ('ec2', 'Amazon EC2'), + ('gce', 'Google Compute Engine'), + ('azure_rm', 'Microsoft Azure Resource Manager'), + ('vmware', 'VMware vCenter'), + ('satellite6', 'Red Hat Satellite 6'), + ('openstack', 'OpenStack'), + ('rhv', 'Red Hat Virtualization'), + ('tower', 'Ansible Tower'), + ('insights', 'Red Hat Insights'), + ], + default=None, + max_length=32, + ), + ), + migrations.AlterField( + model_name='inventoryupdate', + name='source', + field=models.CharField( + choices=[ + ('file', 'File, Directory or Script'), + ('scm', 'Sourced from a Project'), + ('ec2', 'Amazon EC2'), + ('gce', 'Google Compute Engine'), + ('azure_rm', 'Microsoft Azure Resource Manager'), + ('vmware', 'VMware vCenter'), + ('satellite6', 'Red Hat Satellite 6'), + ('openstack', 'OpenStack'), + ('rhv', 'Red Hat Virtualization'), + ('tower', 'Ansible Tower'), + ('insights', 'Red Hat Insights'), + ], + default=None, + max_length=32, + ), + ), + ] diff --git a/awx/main/models/credential/__init__.py b/awx/main/models/credential/__init__.py index f33a0d4671..0e5cac28f0 100644 --- a/awx/main/models/credential/__init__.py +++ b/awx/main/models/credential/__init__.py @@ -954,6 +954,10 @@ ManagedCredentialType( "scm_username": "{{username}}", "scm_password": "{{password}}", }, + 'env': { + 'INSIGHTS_USER': '{{username}}', + 'INSIGHTS_PASSWORD': '{{password}}', + }, }, ) diff --git a/awx/main/models/inventory.py b/awx/main/models/inventory.py index 89a6cb1ad7..a0a30bb844 100644 --- a/awx/main/models/inventory.py +++ b/awx/main/models/inventory.py @@ -503,13 +503,6 @@ class Host(CommonModelNameNotUnique, RelatedJobsMixin): null=True, help_text=_('The date and time ansible_facts was last modified.'), ) - insights_system_id = models.TextField( - blank=True, - default=None, - null=True, - db_index=True, - help_text=_('Red Hat Insights host unique identifier.'), - ) objects = HostManager() @@ -828,6 +821,7 @@ class InventorySourceOptions(BaseModel): ('openstack', _('OpenStack')), ('rhv', _('Red Hat Virtualization')), ('tower', _('Ansible Tower')), + ('insights', _('Red Hat Insights')), ] # From the options of the Django management base command @@ -1548,5 +1542,21 @@ class tower(PluginFileInjector): collection = 'awx' +class insights(PluginFileInjector): + plugin_name = 'insights' + base_injector = 'template' + namespace = 'redhatinsights' + collection = 'insights' + downstream_namespace = 'redhat' + downstream_collection = 'insights' + use_fqcn = 'true' + + def inventory_as_dict(self, inventory_update, private_data_dir): + ret = super(insights, self).inventory_as_dict(inventory_update, private_data_dir) + # this inventory plugin requires the fully qualified inventory plugin name + ret['plugin'] = f'{self.namespace}.{self.collection}.{self.plugin_name}' + return ret + + for cls in PluginFileInjector.__subclasses__(): InventorySourceOptions.injectors[cls.__name__] = cls diff --git a/awx/main/models/jobs.py b/awx/main/models/jobs.py index 70e204c796..c313ab2000 100644 --- a/awx/main/models/jobs.py +++ b/awx/main/models/jobs.py @@ -845,23 +845,6 @@ class Job(UnifiedJob, JobOptions, SurveyJobMixin, JobNotificationMixin, TaskMana continue host.ansible_facts = ansible_facts host.ansible_facts_modified = now() - ansible_local = ansible_facts.get('ansible_local', {}).get('insights', {}) - ansible_facts = ansible_facts.get('insights', {}) - ansible_local_system_id = ansible_local.get('system_id', None) if isinstance(ansible_local, dict) else None - ansible_facts_system_id = ansible_facts.get('system_id', None) if isinstance(ansible_facts, dict) else None - if ansible_local_system_id: - print("Setting local {}".format(ansible_local_system_id)) - logger.debug( - "Insights system_id {} found for host <{}, {}> in" - " ansible local facts".format(ansible_local_system_id, host.inventory.id, host.name) - ) - host.insights_system_id = ansible_local_system_id - elif ansible_facts_system_id: - logger.debug( - "Insights system_id {} found for host <{}, {}> in" - " insights facts".format(ansible_local_system_id, host.inventory.id, host.name) - ) - host.insights_system_id = ansible_facts_system_id host.save() system_tracking_logger.info( 'New fact for inventory {} host {}'.format(smart_str(host.inventory.name), smart_str(host.name)), diff --git a/awx/main/tests/data/insights.json b/awx/main/tests/data/insights.json deleted file mode 100644 index 8a303ba85d..0000000000 --- a/awx/main/tests/data/insights.json +++ /dev/null @@ -1,429 +0,0 @@ -[ - { - "id": 16923675, - "rule": { - "id": 46, - "created_at": "2019-02-07T14:02:34.379375-05:00", - "updated_at": "2019-03-12T11:45:28.804999-04:00", - "ruleset": { - "created_at": "2018-12-20T20:33:00-05:00", - "updated_at": "2018-12-20T20:33:00-05:00", - "rule_source": "https://$REDACTED$/insights-open-source/insights-security", - "description": "Security" - }, - "rule_id": "CVE_2017_5715_cpu_virt|VIRT_CVE_2017_5715_CPU_3_ONLYKERNEL", - "description": "Kernel vulnerable to side-channel attacks in modern microprocessors (CVE-2017-5715/Spectre)", - "active": true, - "category": { - "id": 2, - "name": "Security" - }, - "impact": { - "name": "Information Disclosure", - "impact": 3 - }, - "likelihood": 2, - "node_id": "3244101", - "tags": "security kernel CVE", - "reboot_required": true, - "publish_date": "2018-01-17T12:00:00-05:00", - "summary": "A vulnerability was discovered in modern microprocessors supported by the kernel, whereby an unprivileged attacker can use this flaw to bypass restrictions to gain read access to privileged memory.\nThe issue was reported as [CVE-2017-5715 / Spectre](https://access.redhat.com/security/cve/CVE-2017-5715).\n", - "generic": "An industry-wide issue was found in the manner many modern microprocessors have implemented speculative execution of instructions. There are three primary variants of the issue which differ in the way the speculative execution can be exploited.\n\nAll three rely upon the fact that modern high performance microprocessors implement both speculative execution, and utilize VIPT (Virtually Indexed, Physically Tagged) level 1 data caches that may become allocated with data in the kernel virtual address space during such speculation.\n\nAn unprivileged attacker could use these to read privileged memory by conducting targeted cache side-channel attacks, including memory locations that cross the syscall boundary or the guest/host boundary, or potentially arbitrary host memory addresses.\n", - "reason": "{{? pydata.error_key == \"VIRT_CVE_2017_5715_CPU_3_VIRTKERNEL\" || pydata.error_key == \"VIRT_CVE_2017_5715_CPU_3_DRACUTKERNEL\" }}This machine is vulnerable, because it runs a vulnerable kernel and has the following vulnerable packages installed:\n\n{{~ pydata.PACKAGES :value:index}}\n* `{{= value }}`{{~}}\n{{?}}{{? pydata.error_key == \"VIRT_CVE_2017_5715_CPU_3_ONLYKERNEL\" }}This machine is vulnerable, because it runs a vulnerable kernel.\n{{?}}{{? pydata.error_key == \"VIRT_CVE_2017_5715_CPU_3_ONLYVIRT\" || pydata.error_key == \"VIRT_CVE_2017_5715_CPU_3_ONLYDRACUT\" }}This machine is vulnerable, because it has the following vulnerable packages installed:\n\n{{~ pydata.PACKAGES :value:index}}\n* `{{= value }}`{{~}}\n{{?}}\n\n\n{{? pydata.error_key == \"VIRT_CVE_2017_5715_CPU_3_DRACUTKERNEL\" || pydata.error_key == \"VIRT_CVE_2017_5715_CPU_3_ONLYDRACUT\" }}This machine has a particular family of an AMD processor for which there exists an updated version of Dracut. Dracut is a low-level software for generating an initramfs/initrd image that, among other tasks, selects the appropriate processor microcode to use. It is possible, but not guaranteed, that after updating the affected Dracut packages, the appropriate microcode will be selected to enable the protections for Variant 2 of this issue.\n\n{{?}}\nAn unprivileged attacker could use the vulnerability to read privileged memory by conducting targeted cache side-channel attacks, including memory locations that cross the syscall boundary or the guest/host boundary, or potentially arbitrary host memory addresses.\n", - "more_info": "* For more information about the flaw, see [Kernel Side-Channel Attacks](https://access.redhat.com/security/vulnerabilities/speculativeexecution) and [CVE-2017-5715](https://access.redhat.com/security/cve/CVE-2017-5715).\n* For possible performance impact of kernel updates, see [Speculative Execution Exploit Performance Impacts](https://access.redhat.com/articles/3307751)\n* Extensive details can be found at the [Project Zero blog](https://googleprojectzero.blogspot.ca/2018/01/reading-privileged-memory-with-side.html) and [Meltdown and Spectre Attack webpage](https://meltdownattack.com/).\n* The Customer Portal page for the [Red Hat Security Team](https://access.redhat.com/security/) contains more information about policies, procedures, and alerts for Red Hat products.\n* The Security Team also maintains a frequently updated blog at [securityblog.redhat.com](https://securityblog.redhat.com).\n", - "resolution_set": [ - { - "system_type": 105, - "resolution": "{{? pydata.error_key == \"VIRT_CVE_2017_5715_CPU_3_VIRTKERNEL\" || pydata.error_key == \"VIRT_CVE_2017_5715_CPU_3_DRACUTKERNEL\" }}Red Hat recommends that you update the kernel and the packages and restart the system:\n\n~~~~\n# yum update{{~ pydata.PACKAGE_NAMES :value:index}} {{= value }}{{~}}\n# yum update {{=pydata.kernel_pkg_name}}\n# reboot\n~~~~\n\nIf additional steps to update the kernel are necessary, they are detailed in the separate insights rule *Kernel vulnerable to side-channel attacks in modern microprocessors (CVE-2017-5753/Spectre, CVE-2017-5715/Spectre, CVE-2017-5754/Meltdown)*.\n{{?}}{{? pydata.error_key == \"VIRT_CVE_2017_5715_CPU_3_ONLYKERNEL\" }}Red Hat recommends that you update the kernel:\n\n~~~~\n# yum update {{=pydata.kernel_pkg_name}}\n# reboot\n~~~~\n\nIf additional steps to update the kernel are necessary, they are detailed in the separate insights rule *Kernel vulnerable to side-channel attacks in modern microprocessors (CVE-2017-5753/Spectre, CVE-2017-5715/Spectre, CVE-2017-5754/Meltdown)*.\n{{?}}{{? pydata.error_key == \"VIRT_CVE_2017_5715_CPU_3_ONLYVIRT\" || pydata.error_key == \"VIRT_CVE_2017_5715_CPU_3_ONLYDRACUT\" }}Red Hat recommends that you update the packages and restart the system:\n\n~~~~\n# yum update{{~ pydata.PACKAGE_NAMES :value:index}} {{= value }}{{~}}\n# reboot\n~~~~\n{{?}}\n\nFixes require CPU microcode/firmware to activate.\n\n**In addition:**\n\nSubscribers are advised to contact their hardware OEM to receive the appropriate microcode/firmware for their processor. Red Hat may be providing `microcode_ctl` and `linux_firmware` packages that will cover the limited subset of chipsets we were able to test, but this will **not** address many CPUs that you may have in use in your server fleet. Again, contacting your hardware vendor will ensure you have the appropriate software to enable the protections for Variant 2 of this issue.\n", - "resolution_risk": { - "name": "Upgrade Kernel", - "risk": 3 - }, - "has_playbook": true - } - ], - "total_risk": 2 - }, - "details": { - "type": "rule", - "cves_fail": [ - "CVE-2017-5715" - ], - "cves_pass": [], - "error_key": "VIRT_CVE_2017_5715_CPU_3_ONLYKERNEL", - "kernel_pkg_name": "kernel", - "affected_amd_family": false - }, - "resolution": { - "system_type": 105, - "resolution": "{{? pydata.error_key == \"VIRT_CVE_2017_5715_CPU_3_VIRTKERNEL\" || pydata.error_key == \"VIRT_CVE_2017_5715_CPU_3_DRACUTKERNEL\" }}Red Hat recommends that you update the kernel and the packages and restart the system:\n\n~~~~\n# yum update{{~ pydata.PACKAGE_NAMES :value:index}} {{= value }}{{~}}\n# yum update {{=pydata.kernel_pkg_name}}\n# reboot\n~~~~\n\nIf additional steps to update the kernel are necessary, they are detailed in the separate insights rule *Kernel vulnerable to side-channel attacks in modern microprocessors (CVE-2017-5753/Spectre, CVE-2017-5715/Spectre, CVE-2017-5754/Meltdown)*.\n{{?}}{{? pydata.error_key == \"VIRT_CVE_2017_5715_CPU_3_ONLYKERNEL\" }}Red Hat recommends that you update the kernel:\n\n~~~~\n# yum update {{=pydata.kernel_pkg_name}}\n# reboot\n~~~~\n\nIf additional steps to update the kernel are necessary, they are detailed in the separate insights rule *Kernel vulnerable to side-channel attacks in modern microprocessors (CVE-2017-5753/Spectre, CVE-2017-5715/Spectre, CVE-2017-5754/Meltdown)*.\n{{?}}{{? pydata.error_key == \"VIRT_CVE_2017_5715_CPU_3_ONLYVIRT\" || pydata.error_key == \"VIRT_CVE_2017_5715_CPU_3_ONLYDRACUT\" }}Red Hat recommends that you update the packages and restart the system:\n\n~~~~\n# yum update{{~ pydata.PACKAGE_NAMES :value:index}} {{= value }}{{~}}\n# reboot\n~~~~\n{{?}}\n\nFixes require CPU microcode/firmware to activate.\n\n**In addition:**\n\nSubscribers are advised to contact their hardware OEM to receive the appropriate microcode/firmware for their processor. Red Hat may be providing `microcode_ctl` and `linux_firmware` packages that will cover the limited subset of chipsets we were able to test, but this will **not** address many CPUs that you may have in use in your server fleet. Again, contacting your hardware vendor will ensure you have the appropriate software to enable the protections for Variant 2 of this issue.\n", - "resolution_risk": { - "name": "Upgrade Kernel", - "risk": 3 - }, - "has_playbook": true - } - }, - { - "id": 16923676, - "rule": { - "id": 49, - "created_at": "2019-02-07T14:02:34.410515-05:00", - "updated_at": "2019-03-12T11:45:28.875932-04:00", - "ruleset": { - "created_at": "2018-12-20T20:33:00-05:00", - "updated_at": "2018-12-20T20:33:00-05:00", - "rule_source": "https://$REDACTED$/insights-open-source/insights-security", - "description": "Security" - }, - "rule_id": "CVE_2017_5753_4_cpu_kernel|KERNEL_CVE_2017_5753_4_CPU_ERROR_3", - "description": "Kernel vulnerable to side-channel attacks in modern microprocessors (CVE-2017-5753/Spectre, CVE-2017-5715/Spectre, CVE-2017-5754/Meltdown)", - "active": true, - "category": { - "id": 2, - "name": "Security" - }, - "impact": { - "name": "Information Disclosure", - "impact": 3 - }, - "likelihood": 2, - "node_id": "3244101", - "tags": "security kernel CVE", - "reboot_required": true, - "publish_date": "2018-01-22T12:00:00-05:00", - "summary": "A vulnerability was discovered in modern microprocessors supported by the kernel, whereby an unprivileged attacker can use this flaw to bypass restrictions to gain read access to privileged memory.\nThe issue was reported as [CVE-2017-5753 / CVE-2017-5715 / Spectre](https://access.redhat.com/security/cve/CVE-2017-5753) and [CVE-2017-5754 / Meltdown](https://access.redhat.com/security/cve/CVE-2017-5754).\n", - "generic": "An industry-wide issue was found in the manner many modern microprocessors have implemented speculative execution of instructions. There are three primary variants of the issue which differ in the way the speculative execution can be exploited.\n\nAll three rely upon the fact that modern high performance microprocessors implement both speculative execution, and utilize VIPT (Virtually Indexed, Physically Tagged) level 1 data caches that may become allocated with data in the kernel virtual address space during such speculation.\n\nAn unprivileged attacker could use these to read privileged memory by conducting targeted cache side-channel attacks, including memory locations that cross the syscall boundary or the guest/host boundary, or potentially arbitrary host memory addresses.\n\nMitigations for these vulnerabilities additionally require firmware/microcode updates from hardware vendors.\n", - "reason": "This system is vulnerable to the following variant(s):\n\n{{? pydata.problems.v1_vulnerable}}* Variant 1 (Spectre/CVE-2017-5753)\n{{?}}{{? pydata.problems.v2_vulnerable}}* Variant 2 (Spectre/CVE-2017-5715)\n{{?}}{{? pydata.problems.v3_vulnerable}}* Variant 3 (Meltdown/CVE-2017-5754)\n{{?}}\n\n{{ var factors_contributing_displayed = (!pydata.problems.kernel_supports_features || !pydata.problems.firmware_supports_features || pydata.problems.pti_cmdline_disabled || pydata.problems.ibpb_cmdline_disabled || pydata.problems.ibpb_cmdline_spectre_v2_disabled || pydata.problems.ibrs_cmdline_disabled || pydata.problems.ibrs_cmdline_spectre_v2_disabled || pydata.problems.rfi_flush_cmdline_disabled) ; }}{{? factors_contributing_displayed }}Factors contributing to these vulnerabilities are:\n\n{{? !pydata.problems.kernel_supports_features}}* This system's kernel needs updating.\n{{?}}{{? !pydata.problems.firmware_supports_features}}* This system needs a firmware update.\n{{?}}{{? pydata.problems.pti_cmdline_disabled}}* PTI has been disabled by the `nopti` kernel argument.\n{{?}}{{? pydata.problems.ibpb_cmdline_disabled}}* IBPB has been disabled by the `noibpb` kernel argument.\n{{?}}{{? pydata.problems.ibpb_cmdline_spectre_v2_disabled}}* IBPB has been disabled by the `{{=pydata.problems.spectre_v2_disabling_cmdline}}` kernel argument.\n{{?}}{{? pydata.problems.ibrs_cmdline_disabled}}* IBRS has been disabled by the `noibrs` kernel argument.\n{{?}}{{? pydata.problems.ibrs_cmdline_spectre_v2_disabled}}* IBRS has been disabled by the `{{=pydata.problems.spectre_v2_disabling_cmdline}}` kernel argument.\n{{?}}{{? pydata.problems.rfi_flush_cmdline_disabled}}* RFI flush has been disabled by the `no_rfi_flush` kernel argument.\n{{?}}{{?}}\n\n{{? ( pydata.sysfs_vuln_md && (pydata.sysfs_vuln_md.indexOf(\"Vulnerable\") != -1)) || ( pydata.sysfs_vuln_s2 && (/Vulnerable: Minimal.*ASM retpoline/.test(pydata.sysfs_vuln_s2) || pydata.sysfs_vuln_s2.indexOf(\"Vulnerable: Retpoline without IBPB\") != -1 || pydata.sysfs_vuln_s2.indexOf(\"Vulnerable: Retpoline on Skylake\") != -1 || pydata.sysfs_vuln_s2.indexOf(\"Vulnerable: Retpoline with unsafe module\") != -1)) }}{{? factors_contributing_displayed }}Additional details:{{??}}Factors contributing to these vulnerabilities are:{{?}}\n\n{{? pydata.sysfs_vuln_md }}{{? pydata.sysfs_vuln_md.indexOf(\"Vulnerable\") != -1 }}* The CPU is vulnerable to Variant 3 (Meltdown/CVE-2017-5754) and PTI is disabled.\n{{?}}{{?}}{{? pydata.sysfs_vuln_s2 }}{{? /Vulnerable: Minimal.*ASM retpoline/.test(pydata.sysfs_vuln_s2) }}* The kernel has been compiled with an old version of the `gcc` compiler that doesn't support retpolines, so the kernel is vulnerable to Variant 2 (Spectre/CVE-2017-5715).\n{{?}}{{? pydata.sysfs_vuln_s2.indexOf(\"Vulnerable: Retpoline without IBPB\") != -1 }}* The CPU has vulnerable microcode, so the kernel can't use IBPB to mitigate Variant 2 (Spectre/CVE-2017-5715).\n{{?}}{{? pydata.sysfs_vuln_s2.indexOf(\"Vulnerable: Retpoline on Skylake\") != -1 }}* The CPU is Intel Skylake with updated microcode or newer, but retpolines are enabled. This type of CPU requires that IBRS is enabled and retpolines are disabled. The system is vulnerable to Variant 2 (Spectre/CVE-2017-5715) as a result.\n{{?}}{{? pydata.sysfs_vuln_s2.indexOf(\"Vulnerable: Retpoline with unsafe module\") != -1 }}* A kernel module is loaded that has been compiled with a compiler without retpoline support. As a result, the kernel is vulnerable to Variant 2 (Spectre/CVE-2017-5715).\n{{?}}{{?}}{{?}}\n\n{{? !pydata.debugfs_available || pydata.retpo_kernel_but_no_sys_cpu_vuln }}Some diagnostic information was unavailable to Insights.{{?}}\n{{? !pydata.debugfs_available }}* `debugfs` information was not available. {{? pydata.dmesg_available }}Feature settings were inferred from `dmesg` and known vendor defaults.{{??}}`dmesg` information is also unavailable, so it isn't possible to determine which mitigations are available.{{?}}\n{{?}}{{? pydata.retpo_kernel_but_no_sys_cpu_vuln }}* `/sys/devices/system/cpu/vulnerabilities` was not available to Insights, even though the kernel provides it.\n{{?}}\n\n", - "more_info": "* For more information about the flaws, see [Kernel Side-Channel Attacks](https://access.redhat.com/security/vulnerabilities/speculativeexecution), [CVE-2017-5754](https://access.redhat.com/security/cve/CVE-2017-5754), [CVE-2017-5753](https://access.redhat.com/security/cve/CVE-2017-5753), and [CVE-2017-5715](https://access.redhat.com/security/cve/CVE-2017-5715).\n* For possible performance impact of kernel updates, see [Speculative Execution Exploit Performance Impacts](https://access.redhat.com/articles/3307751).\n* For information related to VMs, see [How do I enable Markdown/Spectre mitigations in my virtualised machines?](https://access.redhat.com/articles/3331571)\n* Extensive details can be found at the [Project Zero blog](https://googleprojectzero.blogspot.ca/2018/01/reading-privileged-memory-with-side.html) and [Meltdown and Spectre Attack webpage](https://meltdownattack.com/).\n* More information about performance impact of the mitigations can be found in the [Speculative Execution Exploit Performance Impacts](https://access.redhat.com/articles/3307751) knowledgebase article.\n* The Customer Portal page for the [Red Hat Security Team](https://access.redhat.com/security/) contains more information about policies, procedures, and alerts for Red Hat products.\n* The Security Team also maintains a frequently updated blog at [securityblog.redhat.com](https://securityblog.redhat.com).\n", - "resolution_set": [ - { - "system_type": 105, - "resolution": "{{? pydata.dmesg_wrapped || !pydata.debugfs_available || pydata.retpo_kernel_but_no_sys_cpu_vuln }}**To improve detection reliability:**{{?}}\n{{? pydata.dmesg_wrapped}}* The kernel ring buffer has wrapped, making some information from `dmesg` unreliable. See [How do I increase the kernel log ring buffer size?](https://access.redhat.com/solutions/47276) for further information. Rebooting the system may also overcome this issue.\n{{?}}{{? !pydata.debugfs_available}}* Kernel debug information was not available. Mount `debugfs` as follows: \n{{? pydata.release >= 7 }}\n ~~~\n # systemctl restart sys-kernel-debug.mount\n ~~~\n{{??}}\n ~~~\n # mount -t debugfs nodev /sys/kernel/debug\n ~~~\n{{?}}{{?}}{{? pydata.retpo_kernel_but_no_sys_cpu_vuln}}* Allow Insights to collect `/sys/devices/system/cpu/vulnerabilities`.\n{{?}}\n\n**To mitigate the vulnerability:**\n{{? ( pydata.dmesg_wrapped || !pydata.dmesg_available ) && !pydata.debugfs_available && (!pydata.sysfs_vuln_s1 && !pydata.sysfs_vuln_s2 && !pydata.sysfs_vuln_md) }}* It might be necessary to improve detection reliability before we can offer more concrete mitigation steps.{{? !pydata.problems.kernel_supports_features}} If it is not possible, update the kernel package and reboot:\n ~~~\n # yum update {{=pydata.package_name}}\n # reboot\n ~~~\n{{?}}\n{{?}}{{? pydata.dmesg_available && !pydata.problems.kernel_supports_features }}* This system is running a vulnerable kernel. Update the kernel package and reboot:\n ~~~\n # yum update {{=pydata.package_name}}\n # reboot\n ~~~\n{{?}}{{? ((pydata.debugfs_available || pydata.dmesg_available) && !pydata.problems.firmware_supports_features) || (pydata.sysfs_vuln_s2 && (pydata.sysfs_vuln_s2.indexOf(\"Vulnerable: Retpoline without IBPB\") != -1)) }}* This system needs a firmware update. Contact your system hardware vendor for more information.\n{{?}}{{? pydata.problems.pti_cmdline_disabled }}* Remove the `nopti` kernel argument:\n ~~~\n # grubby --update-kernel=ALL --remove-args=nopti\n ~~~\n{{?}}{{? pydata.problems.ibpb_cmdline_disabled }}* Remove the `noibpb` kernel argument:\n ~~~\n # grubby --update-kernel=ALL --remove-args=noibpb\n ~~~\n{{?}}{{? pydata.problems.ibrs_cmdline_disabled }}* Remove the `noibrs` kernel argument:\n ~~~\n # grubby --update-kernel=ALL --remove-args=noibrs\n ~~~\n{{?}}{{? ( pydata.problems.ibpb_cmdline_spectre_v2_disabled || pydata.problems.ibrs_cmdline_spectre_v2_disabled ) && ( pydata.problems.spectre_v2_disabling_cmdline.indexOf(\"v2=\") != -1 ) }}* Remove the `spectre_v2` kernel argument:\n ~~~\n # grubby --update-kernel=ALL --remove-args=spectre_v2\n ~~~\n{{?}}{{? ( pydata.problems.ibpb_cmdline_spectre_v2_disabled || pydata.problems.ibrs_cmdline_spectre_v2_disabled ) && ( pydata.problems.spectre_v2_disabling_cmdline.indexOf(\"nospectre\") != -1 ) }}* Remove the `nospectre_v2` kernel argument:\n ~~~\n # grubby --update-kernel=ALL --remove-args=nospectre_v2\n ~~~\n{{?}}{{? pydata.problems.rfi_flush_cmdline_disabled }}* Remove the `no_rfi_flush` kernel argument:\n ~~~\n # grubby --update-kernel=ALL --remove-args=no_rfi_flush\n ~~~\n{{?}}{{? pydata.sysfs_vuln_s2 }}{{? /Vulnerable: Minimal.*ASM retpoline/.test(pydata.sysfs_vuln_s2) }}* This system is running a custom kernel that has been compiled with outdated `gcc`. Either recompile the kernel with retpoline-enabled `gcc` or update the kernel package and reboot:\n ~~~\n # yum update {{=pydata.package_name}}\n # reboot\n ~~~\n{{?}}{{? pydata.sysfs_vuln_s2.indexOf(\"Vulnerable: Retpoline on Skylake\") != -1 }}* Enable IBRS and disable retpolines. Retpolines are disabled automatically when IBRS is enabled:\n ~~~\n # echo 1 > /sys/kernel/debug/x86/ibrs_enabled\n ~~~\n{{?}}{{? pydata.sysfs_vuln_s2.indexOf(\"Vulnerable: Retpoline with unsafe module\") != -1 }}* Unload the kernel modules that have been compiled without a retpoline-enabled compiler. To find the affected kernel modules:\n ~~~\n # dmesg | grep \"WARNING: module.*built without retpoline-enabled compiler\"\n ~~~\nTo unload the module:\n ~~~\n # modprobe -r module_name\n ~~~\n{{?}}{{?}}{{? pydata.problems.pti_cmdline_disabled || pydata.problems.ibpb_cmdline_disabled || pydata.problems.ibrs_cmdline_disabled || pydata.problems.ibpb_cmdline_spectre_v2_disabled || pydata.problems.ibrs_cmdline_spectre_v2_disabled || pydata.problems.rfi_flush_cmdline_disabled }}\n ~~~\n # reboot\n ~~~\n {{?}}\n\n{{? !pydata.virtual == false || pydata.virtual === null }}**Note about virtualization**\n\nIn virtualized environment, there are more steps to mitigate the issue, including:\n* Host needs to have updated kernel and CPU microcode\n* Host needs to have updated virtualization software\n* Guest needs to have updated kernel\n* Hypervisor needs to propagate new CPU features correctly\n\nFor more details about mitigations in virtualized environment see: [https://access.redhat.com/articles/3331571](https://access.redhat.com/articles/3331571){{?}}\n\n{{? pydata.virtual == \"vmware\" }}For help with setting VMWare to propagate CPU features correctly, refer to the following knowledge-base article: [https://kb.vmware.com/s/article/52085](https://kb.vmware.com/s/article/52085){{?}}\n", - "resolution_risk": { - "name": "Upgrade Kernel", - "risk": 3 - }, - "has_playbook": true - } - ], - "total_risk": 2 - }, - "details": { - "mfr": "Intel", - "type": "rule", - "virtual": "kvm", - "problems": { - "v1_vulnerable": false, - "v2_vulnerable": true, - "v3_vulnerable": false, - "pti_cmdline_disabled": false, - "ibpb_cmdline_disabled": false, - "ibrs_cmdline_disabled": false, - "kernel_supports_features": true, - "firmware_supports_features": false, - "rfi_flush_cmdline_disabled": false, - "spectre_v2_disabling_cmdline": null, - "ibpb_cmdline_spectre_v2_disabled": false, - "ibrs_cmdline_spectre_v2_disabled": false - }, - "cves_fail": [ - "CVE-2017-5715" - ], - "cves_pass": [ - "CVE-2017-5753", - "CVE-2017-5754" - ], - "error_key": "KERNEL_CVE_2017_5753_4_CPU_ERROR_3", - "package_name": "kernel", - "dmesg_wrapped": false, - "release_major": "7", - "sysfs_vuln_md": "Mitigation: PTI", - "sysfs_vuln_s1": "Mitigation: Load fences, __user pointer sanitization", - "sysfs_vuln_s2": "Vulnerable: Retpoline without IBPB", - "running_kernel": "3.10.0-862.14.4.el7.x86_64", - "dmesg_available": true, - "debugfs_available": true, - "old_specs_on_client": false, - "retpo_kernel_but_no_sys_cpu_vuln": false - }, - "resolution": { - "system_type": 105, - "resolution": "{{? pydata.dmesg_wrapped || !pydata.debugfs_available || pydata.retpo_kernel_but_no_sys_cpu_vuln }}**To improve detection reliability:**{{?}}\n{{? pydata.dmesg_wrapped}}* The kernel ring buffer has wrapped, making some information from `dmesg` unreliable. See [How do I increase the kernel log ring buffer size?](https://access.redhat.com/solutions/47276) for further information. Rebooting the system may also overcome this issue.\n{{?}}{{? !pydata.debugfs_available}}* Kernel debug information was not available. Mount `debugfs` as follows: \n{{? pydata.release >= 7 }}\n ~~~\n # systemctl restart sys-kernel-debug.mount\n ~~~\n{{??}}\n ~~~\n # mount -t debugfs nodev /sys/kernel/debug\n ~~~\n{{?}}{{?}}{{? pydata.retpo_kernel_but_no_sys_cpu_vuln}}* Allow Insights to collect `/sys/devices/system/cpu/vulnerabilities`.\n{{?}}\n\n**To mitigate the vulnerability:**\n{{? ( pydata.dmesg_wrapped || !pydata.dmesg_available ) && !pydata.debugfs_available && (!pydata.sysfs_vuln_s1 && !pydata.sysfs_vuln_s2 && !pydata.sysfs_vuln_md) }}* It might be necessary to improve detection reliability before we can offer more concrete mitigation steps.{{? !pydata.problems.kernel_supports_features}} If it is not possible, update the kernel package and reboot:\n ~~~\n # yum update {{=pydata.package_name}}\n # reboot\n ~~~\n{{?}}\n{{?}}{{? pydata.dmesg_available && !pydata.problems.kernel_supports_features }}* This system is running a vulnerable kernel. Update the kernel package and reboot:\n ~~~\n # yum update {{=pydata.package_name}}\n # reboot\n ~~~\n{{?}}{{? ((pydata.debugfs_available || pydata.dmesg_available) && !pydata.problems.firmware_supports_features) || (pydata.sysfs_vuln_s2 && (pydata.sysfs_vuln_s2.indexOf(\"Vulnerable: Retpoline without IBPB\") != -1)) }}* This system needs a firmware update. Contact your system hardware vendor for more information.\n{{?}}{{? pydata.problems.pti_cmdline_disabled }}* Remove the `nopti` kernel argument:\n ~~~\n # grubby --update-kernel=ALL --remove-args=nopti\n ~~~\n{{?}}{{? pydata.problems.ibpb_cmdline_disabled }}* Remove the `noibpb` kernel argument:\n ~~~\n # grubby --update-kernel=ALL --remove-args=noibpb\n ~~~\n{{?}}{{? pydata.problems.ibrs_cmdline_disabled }}* Remove the `noibrs` kernel argument:\n ~~~\n # grubby --update-kernel=ALL --remove-args=noibrs\n ~~~\n{{?}}{{? ( pydata.problems.ibpb_cmdline_spectre_v2_disabled || pydata.problems.ibrs_cmdline_spectre_v2_disabled ) && ( pydata.problems.spectre_v2_disabling_cmdline.indexOf(\"v2=\") != -1 ) }}* Remove the `spectre_v2` kernel argument:\n ~~~\n # grubby --update-kernel=ALL --remove-args=spectre_v2\n ~~~\n{{?}}{{? ( pydata.problems.ibpb_cmdline_spectre_v2_disabled || pydata.problems.ibrs_cmdline_spectre_v2_disabled ) && ( pydata.problems.spectre_v2_disabling_cmdline.indexOf(\"nospectre\") != -1 ) }}* Remove the `nospectre_v2` kernel argument:\n ~~~\n # grubby --update-kernel=ALL --remove-args=nospectre_v2\n ~~~\n{{?}}{{? pydata.problems.rfi_flush_cmdline_disabled }}* Remove the `no_rfi_flush` kernel argument:\n ~~~\n # grubby --update-kernel=ALL --remove-args=no_rfi_flush\n ~~~\n{{?}}{{? pydata.sysfs_vuln_s2 }}{{? /Vulnerable: Minimal.*ASM retpoline/.test(pydata.sysfs_vuln_s2) }}* This system is running a custom kernel that has been compiled with outdated `gcc`. Either recompile the kernel with retpoline-enabled `gcc` or update the kernel package and reboot:\n ~~~\n # yum update {{=pydata.package_name}}\n # reboot\n ~~~\n{{?}}{{? pydata.sysfs_vuln_s2.indexOf(\"Vulnerable: Retpoline on Skylake\") != -1 }}* Enable IBRS and disable retpolines. Retpolines are disabled automatically when IBRS is enabled:\n ~~~\n # echo 1 > /sys/kernel/debug/x86/ibrs_enabled\n ~~~\n{{?}}{{? pydata.sysfs_vuln_s2.indexOf(\"Vulnerable: Retpoline with unsafe module\") != -1 }}* Unload the kernel modules that have been compiled without a retpoline-enabled compiler. To find the affected kernel modules:\n ~~~\n # dmesg | grep \"WARNING: module.*built without retpoline-enabled compiler\"\n ~~~\nTo unload the module:\n ~~~\n # modprobe -r module_name\n ~~~\n{{?}}{{?}}{{? pydata.problems.pti_cmdline_disabled || pydata.problems.ibpb_cmdline_disabled || pydata.problems.ibrs_cmdline_disabled || pydata.problems.ibpb_cmdline_spectre_v2_disabled || pydata.problems.ibrs_cmdline_spectre_v2_disabled || pydata.problems.rfi_flush_cmdline_disabled }}\n ~~~\n # reboot\n ~~~\n {{?}}\n\n{{? !pydata.virtual == false || pydata.virtual === null }}**Note about virtualization**\n\nIn virtualized environment, there are more steps to mitigate the issue, including:\n* Host needs to have updated kernel and CPU microcode\n* Host needs to have updated virtualization software\n* Guest needs to have updated kernel\n* Hypervisor needs to propagate new CPU features correctly\n\nFor more details about mitigations in virtualized environment see: [https://access.redhat.com/articles/3331571](https://access.redhat.com/articles/3331571){{?}}\n\n{{? pydata.virtual == \"vmware\" }}For help with setting VMWare to propagate CPU features correctly, refer to the following knowledge-base article: [https://kb.vmware.com/s/article/52085](https://kb.vmware.com/s/article/52085){{?}}\n", - "resolution_risk": { - "name": "Upgrade Kernel", - "risk": 3 - }, - "has_playbook": true - } - }, - { - "id": 16923673, - "rule": { - "id": 72, - "created_at": "2019-02-07T14:02:34.653624-05:00", - "updated_at": "2019-03-12T11:45:29.372525-04:00", - "ruleset": { - "created_at": "2018-12-20T20:33:00-05:00", - "updated_at": "2018-12-20T20:33:00-05:00", - "rule_source": "https://$REDACTED$/insights-open-source/insights-security", - "description": "Security" - }, - "rule_id": "CVE_2018_3639_cpu_kernel|CVE_2018_3639_CPU_BAD_MICROCODE", - "description": "Kernel vulnerable to side-channel attacks in modern microprocessors using Speculative Store Bypass when CPU microcode is outdated (CVE-2018-3639)", - "active": true, - "category": { - "id": 2, - "name": "Security" - }, - "impact": { - "name": "Local Privilege Escalation", - "impact": 2 - }, - "likelihood": 2, - "node_id": "3448801", - "tags": "security", - "reboot_required": true, - "publish_date": "2018-05-21T21:00:00-04:00", - "summary": "An industry-wide issue was found in the manner in which many modern microprocessors have implemented speculative execution of instructions. It has been assigned [CVE-2018-3639](https://access.redhat.com/security/cve/CVE-2018-3639). Mitigations for this vulnerability require firmware/microcode updates from hardware vendors.\n\nAn unprivileged attacker can use this flaw to bypass restrictions in order to gain read access to privileged memory that would otherwise be inaccessible.\n", - "generic": "An industry-wide issue was found in the manner in which many modern microprocessors have implemented speculative execution of instructions. The flaw is similar to CVE-2017-5753 (aka \"Spectre v1\"), except it leverages Speculative Store Bypass memory optimization in place of the Branch Misprediction used by Spectre v1.\n\nAn unprivileged attacker can use this flaw to bypass restrictions in order to gain read access to privileged memory that would otherwise be inaccessible, e.g. to memory outside of a sandboxed environments like web browsers or JIT execution runtimes.\n\nMitigations for this vulnerability require firmware/microcode updates from hardware vendors.\n\nRed Hat recommends that you update the kernel and update firmware.\n", - "reason": "The system is vulnerable because:\n\n* CPU microcode requires an update\n", - "more_info": "* For more information about the flaw, see [the vulnerability article](https://access.redhat.com/security/vulnerabilities/ssbd).\n* To learn how to upgrade packages, see [What is yum and how do I use it?](https://access.redhat.com/solutions/9934).\n* The Customer Portal page for the [Red Hat Security Team](https://access.redhat.com/security/) contains more information about policies, procedures, and alerts for Red Hat products.\n* The Security Team also maintains a frequently updated blog at [securityblog.redhat.com](https://securityblog.redhat.com).\n", - "resolution_set": [ - { - "system_type": 105, - "resolution": "This system needs a firmware update. Contact your system hardware vendor for more information.\n\n{{? !pydata.virtual == false || pydata.virtual === null}}**Note about virtualization**\n\nIn virtualized environment, there are more steps to mitigate the issue, including:\n* Host needs to have updated kernel and CPU microcode\n* Host needs to have updated virtualization software\n* Guest needs to have updated kernel\n* Hypervisor needs to propagate new CPU features correctly\n\nFor more details about mitigations in virtualized environment see: [https://access.redhat.com/articles/3331571](https://access.redhat.com/articles/3331571){{?}}\n\n{{? pydata.virtual == \"vmware\" }}For help with setting VMWare to propagate CPU features correctly, refer to the following knowledge-base article: [https://kb.vmware.com/s/article/52085](https://kb.vmware.com/s/article/52085){{?}}\n", - "resolution_risk": { - "name": "Hardware Vendor Firmware Update", - "risk": 3 - }, - "has_playbook": false - } - ], - "total_risk": 2 - }, - "details": { - "rt": false, - "type": "rule", - "virtual": "kvm", - "cmd_avail": true, - "cves_fail": [ - "CVE-2018-3639" - ], - "cves_pass": [], - "error_key": "CVE_2018_3639_CPU_BAD_MICROCODE", - "running_kernel": "3.10.0-862.14.4.el7.x86_64", - "vuln_file_present": true - }, - "resolution": { - "system_type": 105, - "resolution": "This system needs a firmware update. Contact your system hardware vendor for more information.\n\n{{? !pydata.virtual == false || pydata.virtual === null}}**Note about virtualization**\n\nIn virtualized environment, there are more steps to mitigate the issue, including:\n* Host needs to have updated kernel and CPU microcode\n* Host needs to have updated virtualization software\n* Guest needs to have updated kernel\n* Hypervisor needs to propagate new CPU features correctly\n\nFor more details about mitigations in virtualized environment see: [https://access.redhat.com/articles/3331571](https://access.redhat.com/articles/3331571){{?}}\n\n{{? pydata.virtual == \"vmware\" }}For help with setting VMWare to propagate CPU features correctly, refer to the following knowledge-base article: [https://kb.vmware.com/s/article/52085](https://kb.vmware.com/s/article/52085){{?}}\n", - "resolution_risk": { - "name": "Hardware Vendor Firmware Update", - "risk": 3 - }, - "has_playbook": false - } - }, - { - "id": 16923678, - "rule": { - "id": 193, - "created_at": "2019-02-07T14:02:35.803497-05:00", - "updated_at": "2019-02-07T14:02:35.803513-05:00", - "ruleset": { - "created_at": "2018-12-20T20:33:00-05:00", - "updated_at": "2018-12-20T20:33:00-05:00", - "rule_source": "https://$REDACTED$/insights-open-source/insights-security", - "description": "Security" - }, - "rule_id": "hardening_httpd_pci_dss|HARDENING_HTTPD_PCI_DSS", - "description": "Decreased security in httpd when using deprecated TLS protocol version (PCI DSS)", - "active": true, - "category": { - "id": 2, - "name": "Security" - }, - "impact": { - "name": "Hardening", - "impact": 1 - }, - "likelihood": 1, - "node_id": "", - "tags": "httpd hardening security", - "reboot_required": false, - "publish_date": "2018-10-20T00:00:00-04:00", - "summary": "PCI Data Security Standard [mandates disabling](https://blog.pcisecuritystandards.org/are-you-ready-for-30-june-2018-sayin-goodbye-to-ssl-early-tls) TLS versions older than 1.1 for safeguarding payment data.\n", - "generic": "These hosts are running httpd with legacy SSL or TLS versions enabled and might be non-compliant with the PCI Data Security Standard.\n\nRed Hat recommends that you change your httpd/Apache configuration files. Select a host to see the host-specific details that need to be updated within the httpd/Apache configuration.\n", - "reason": "This host is running httpd with legacy SSL or TLS versions enabled and might be non-compliant with the PCI Data Security Standard.\n", - "more_info": "* For more information about the new PCI DSS rules, see [the article](https://blog.pcisecuritystandards.org/are-you-ready-for-30-june-2018-sayin-goodbye-to-ssl-early-tls)\n* [How do I globally disable TLSv1.0 on my RHEL server?](https://access.redhat.com/solutions/2157131)\n* The Customer Portal page for the [Red Hat Security Team](https://access.redhat.com/security/) contains more information about policies, procedures, and alerts for Red Hat Products.\n* The Security Team also maintains a frequently updated blog at [securityblog.redhat.com](https://securityblog.redhat.com).\n", - "resolution_set": [ - { - "system_type": 19, - "resolution": "Red Hat recommends that you disable SSLv3 and TLSv1.0.\n\n{{?pydata.ssl_protocols}}**The following SSLProtocol directive(s) in the respective Apache configuration files will need to be modified:**\n
    {{~pydata.ssl_protocols: ssl}}\n
  • `{{=ssl[0]}}` - `{{=ssl[1]}}`
  • \n{{~}}
\n\nRed Hat recommends that either of these `SSLProtocol` configurations is used:\n* `SSLProtocol -all +TLSv1.1 +TLSv1.2`\n* `SSLProtocol all -SSLv2 -SSLv3 -TLSv1`\n\nFor additional hardening, to use TLS 1.2 only, use either of these `SSLProtocol` configurations:\n* `SSLProtocol -all +TLSv1.2`\n* `SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1`\n{{?}}\n\n{{?pydata.nss_protocols}}**The following NSSProtocol directive(s) in the respective Apache configuration files will need to be modified:**\n
    {{~pydata.nss_protocols: nss}}\n
  • `{{=nss[0]}}` - `{{=nss[1]}}`
  • \n{{~}}
\n\nRed Hat recommends that this `NSSProtocol` configuration is used:\n* `NSSProtocol TLSv1.1,TLSv1.2`\n\nFor additional hardening, to use TLS 1.2 only, use the following `NSSProtocol` configuration:\n* `NSSProtocol TLSv1.2`\n{{?}}\n\nThen restart httpd:\n\n # service httpd restart\n\nMake the changes permanent in the container image by using the **docker commit** command at the next container shutdown.\n\n{{?pydata.scl_installed}}You have installed httpd from Software Collections, which uses a different configuration files path from the default httpd. Some of the detected configuration files might be applicable only if the respective httpd package is used. Red Hat recommends resolving all listed issues to prevent inadvertent exposure to the vulnerability in case the httpd you use changes in the future.\n{{?}}\n", - "resolution_risk": { - "name": "Update Service Configuration", - "risk": 3 - }, - "has_playbook": false - }, - { - "system_type": 105, - "resolution": "Red Hat recommends that you disable SSLv3 and TLSv1.0.\n\n{{?pydata.ssl_protocols}}**The following SSLProtocol directive(s) in the respective Apache configuration files will need to be modified:**\n{{~pydata.ssl_protocols: ssl}}* `{{=ssl[0]}}` - {{=ssl[1]}}\n{{~}}\n\nRed Hat recommends that either of these `SSLProtocol` configurations is used:\n* `SSLProtocol -all +TLSv1.1 +TLSv1.2`\n* `SSLProtocol all -SSLv2 -SSLv3 -TLSv1`\n\nFor additional hardening, to use TLS 1.2 only, use either of these `SSLProtocol` configurations:\n* `SSLProtocol -all +TLSv1.2`\n* `SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1`\n{{?}}\n\n{{?pydata.nss_protocols}}**The following NSSProtocol directive(s) in the respective Apache configuration files will need to be modified:**\n{{~pydata.nss_protocols: nss}}* `{{=nss[0]}}` - {{=nss[1]}}\n{{~}}\n\nRed Hat recommends that this `NSSProtocol` configuration is used:\n* `NSSProtocol TLSv1.1,TLSv1.2`\n\nFor additional hardening, to use TLS 1.2 only, use the following `NSSProtocol` configuration:\n* `NSSProtocol TLSv1.2`\n{{?}}\n\nThen restart httpd:\n\n # service httpd restart\n\n{{?pydata.scl_installed}}You have installed httpd from Software Collections, which uses a different configuration files path from the default httpd. Some of the detected configuration files might be applicable only if the respective httpd package is used. Red Hat recommends resolving all listed issues to prevent inadvertent exposure to the vulnerability in case the httpd you use changes in the future.\n{{?}}\n", - "resolution_risk": { - "name": "Update Service Configuration", - "risk": 3 - }, - "has_playbook": false - }, - { - "system_type": 29, - "resolution": "Red Hat recommends that you disable SSLv3 and TLSv1.0.\n\n{{?pydata.ssl_protocols}}**The following SSLProtocol directive(s) in the respective Apache configuration files will need to be modified:**\n
    {{~pydata.ssl_protocols: ssl}}\n
  • `{{=ssl[0]}}` - `{{=ssl[1]}}`
  • \n{{~}}
\n\nRed Hat recommends that either of these `SSLProtocol` configurations is used:\n* `SSLProtocol -all +TLSv1.1 +TLSv1.2`\n* `SSLProtocol all -SSLv2 -SSLv3 -TLSv1`\n\nFor additional hardening, to use TLS 1.2 only, use either of these `SSLProtocol` configurations:\n* `SSLProtocol -all +TLSv1.2`\n* `SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1`\n{{?}}\n\n{{?pydata.nss_protocols}}**The following NSSProtocol directive(s) in the respective Apache configuration files will need to be modified:**\n
    {{~pydata.nss_protocols: nss}}\n
  • `{{=nss[0]}}` - `{{=nss[1]}}`
  • \n{{~}}
\n\nRed Hat recommends that this `NSSProtocol` configuration is used:\n* `NSSProtocol TLSv1.1,TLSv1.2`\n\nFor additional hardening, to use TLS 1.2 only, use the following `NSSProtocol` configuration:\n* `NSSProtocol TLSv1.2`\n{{?}}\n\nMake the changes permanent in the image by using the **docker commit** command.\n\n{{?pydata.scl_installed}}You have installed httpd from Software Collections, which uses a different configuration files path from the default httpd. Some of the detected configuration files might be applicable only if the respective httpd package is used. Red Hat recommends resolving all listed issues to prevent inadvertent exposure to the vulnerability in case the httpd you use changes in the future.\n{{?}}\n", - "resolution_risk": { - "name": "Update Service Configuration", - "risk": 3 - }, - "has_playbook": false - } - ], - "total_risk": 1 - }, - "details": { - "type": "rule", - "error_key": "HARDENING_HTTPD_PCI_DSS", - "nss_protocols": [ - [ - "NSSProtocol TLSv1.0,TLSv1.1,TLSv1.2", - "/etc/httpd/conf.d/nss.conf" - ] - ], - "scl_installed": false, - "ssl_protocols": null - }, - "resolution": { - "system_type": 105, - "resolution": "Red Hat recommends that you disable SSLv3 and TLSv1.0.\n\n{{?pydata.ssl_protocols}}**The following SSLProtocol directive(s) in the respective Apache configuration files will need to be modified:**\n{{~pydata.ssl_protocols: ssl}}* `{{=ssl[0]}}` - {{=ssl[1]}}\n{{~}}\n\nRed Hat recommends that either of these `SSLProtocol` configurations is used:\n* `SSLProtocol -all +TLSv1.1 +TLSv1.2`\n* `SSLProtocol all -SSLv2 -SSLv3 -TLSv1`\n\nFor additional hardening, to use TLS 1.2 only, use either of these `SSLProtocol` configurations:\n* `SSLProtocol -all +TLSv1.2`\n* `SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1`\n{{?}}\n\n{{?pydata.nss_protocols}}**The following NSSProtocol directive(s) in the respective Apache configuration files will need to be modified:**\n{{~pydata.nss_protocols: nss}}* `{{=nss[0]}}` - {{=nss[1]}}\n{{~}}\n\nRed Hat recommends that this `NSSProtocol` configuration is used:\n* `NSSProtocol TLSv1.1,TLSv1.2`\n\nFor additional hardening, to use TLS 1.2 only, use the following `NSSProtocol` configuration:\n* `NSSProtocol TLSv1.2`\n{{?}}\n\nThen restart httpd:\n\n # service httpd restart\n\n{{?pydata.scl_installed}}You have installed httpd from Software Collections, which uses a different configuration files path from the default httpd. Some of the detected configuration files might be applicable only if the respective httpd package is used. Red Hat recommends resolving all listed issues to prevent inadvertent exposure to the vulnerability in case the httpd you use changes in the future.\n{{?}}\n", - "resolution_risk": { - "name": "Update Service Configuration", - "risk": 3 - }, - "has_playbook": false - } - }, - { - "id": 16923677, - "rule": { - "id": 235, - "created_at": "2019-02-07T14:02:36.236195-05:00", - "updated_at": "2019-02-11T15:21:37.409742-05:00", - "ruleset": { - "created_at": "2018-05-21T22:00:51-04:00", - "updated_at": "2018-05-21T22:00:51-04:00", - "rule_source": "https://$REDACTED$/insights-open-source/insights-plugins", - "description": "Advisor" - }, - "rule_id": "httpd24_deprecated_order|DEPRECATED_ORDER_USED_INFO_V1", - "description": "Unexpected behavior when using deprecated access control directives in httpd 2.4", - "active": true, - "category": { - "id": 1, - "name": "Availability" - }, - "impact": { - "name": "Invalid Configuration", - "impact": 1 - }, - "likelihood": 3, - "node_id": "", - "tags": "sbr_webservers webservers httpd", - "reboot_required": false, - "publish_date": "2018-05-30T20:39:00-04:00", - "summary": "The httpd service does not work as expected when using old directives in httpd-2.4.\n", - "generic": "Access control is using deprecated directives (\"Order\", \"Allow\" and \"Deny\") provided by `mod_authz_compat` which has been replaced by `mod_authz_host` in **httpd 2.4**.\n", - "reason": "This host is running **{{=pydata.ver}}** and using the following old directives (`Order`, `Allow` or `Deny`) which have been deprecated:\n\n{{ for (var _sec in pydata.dep_conf) { }}\n* Section `<{{=_sec}}>`\n {{ for (var file in pydata.dep_conf[_sec]) { }} * Configuration file `{{=file}}`\n ```text {{ for (var dir in pydata.dep_conf[_sec][file]) { }}\n {{=pydata.dep_conf[_sec][file][dir]}} {{ } }}\n ```\n {{ } }}\n{{ } }}\n", - "more_info": "", - "resolution_set": [ - { - "system_type": 105, - "resolution": "Red Hat recommends that you replace the old directives (\"Order\", \"Allow\" and \"Deny\") with the new directive (\"Require\") in **httpd-2.4**. Please check the [Upgrading to 2.4 from 2.2](http://httpd.apache.org/docs/2.4/upgrading.html) guide for more information.\n", - "resolution_risk": { - "name": "Update Service Configuration", - "risk": 3 - }, - "has_playbook": false - } - ], - "total_risk": 2 - }, - "details": { - "ver": "httpd-2.4.6-80.el7", - "type": "rule", - "dep_conf": { - "Location /KdcProxy": { - "/etc/httpd/conf.d/ipa-kdc-proxy.conf": [ - "Order Deny,Allow", - "Allow from all" - ] - }, - "Directory /usr/share/fonts": { - "/etc/httpd/conf.d/ipa.conf": [ - "Allow from all" - ] - }, - "Directory /usr/share/ipa/ui": { - "/etc/httpd/conf.d/ipa.conf": [ - "Allow from all" - ] - }, - "Directory /usr/share/ipa/html": { - "/etc/httpd/conf.d/ipa.conf": [ - "Allow from all" - ] - }, - "Directory /usr/share/ipa/wsgi": { - "/etc/httpd/conf.d/ipa.conf": [ - "Allow from all" - ] - }, - "Location /ipa/session/sync_token": { - "/etc/httpd/conf.d/ipa.conf": [ - "Order Deny,Allow", - "Allow from all" - ] - }, - "Directory /usr/share/ipa/migration": { - "/etc/httpd/conf.d/ipa.conf": [ - "Allow from all" - ] - }, - "Location /ipa/session/login_password": { - "/etc/httpd/conf.d/ipa.conf": [ - "Order Deny,Allow", - "Allow from all" - ] - }, - "Directory /var/lib/ipa/pki-ca/publish": { - "/etc/httpd/conf.d/ipa.conf": [ - "Allow from all" - ] - }, - "Location /ipa/session/change_password": { - "/etc/httpd/conf.d/ipa.conf": [ - "Order Deny,Allow", - "Allow from all" - ] - } - }, - "error_key": "DEPRECATED_ORDER_USED_INFO_V1" - }, - "resolution": { - "system_type": 105, - "resolution": "Red Hat recommends that you replace the old directives (\"Order\", \"Allow\" and \"Deny\") with the new directive (\"Require\") in **httpd-2.4**. Please check the [Upgrading to 2.4 from 2.2](http://httpd.apache.org/docs/2.4/upgrading.html) guide for more information.\n", - "resolution_risk": { - "name": "Update Service Configuration", - "risk": 3 - }, - "has_playbook": false - } - } -] diff --git a/awx/main/tests/data/insights.py b/awx/main/tests/data/insights.py deleted file mode 100644 index f51f9e18f3..0000000000 --- a/awx/main/tests/data/insights.py +++ /dev/null @@ -1,14 +0,0 @@ -import json -import os - - -dir_path = os.path.dirname(os.path.realpath(__file__)) - -with open(os.path.join(dir_path, 'insights_hosts.json')) as data_file: - TEST_INSIGHTS_HOSTS = json.load(data_file) - -with open(os.path.join(dir_path, 'insights.json')) as data_file: - TEST_INSIGHTS_PLANS = json.load(data_file) - -with open(os.path.join(dir_path, 'insights_remediations.json')) as data_file: - TEST_INSIGHTS_REMEDIATIONS = json.load(data_file)['data'] diff --git a/awx/main/tests/data/insights_hosts.json b/awx/main/tests/data/insights_hosts.json deleted file mode 100644 index 8228222854..0000000000 --- a/awx/main/tests/data/insights_hosts.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "total": 1, - "count": 1, - "page": 1, - "per_page": 50, - "results": [ - { - "id": "11111111-1111-1111-1111-111111111111", - "insights_id": "22222222-2222-2222-2222-222222222222", - "updated": "2019-03-19T21:59:09.213151-04:00" - } - ] -} diff --git a/awx/main/tests/data/insights_remediations.json b/awx/main/tests/data/insights_remediations.json deleted file mode 100644 index 17a2fb1541..0000000000 --- a/awx/main/tests/data/insights_remediations.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "data": [ - { - "id": "9197ba55-0abc-4028-9bbe-269e530f8bd5", - "name": "Fix Critical CVEs", - "created_by": { - "username": "jharting@redhat.com", - "first_name": "Jozef", - "last_name": "Hartinger" - }, - "created_at": "2018-12-05T08:19:36.641Z", - "updated_by": { - "username": "jharting@redhat.com", - "first_name": "Jozef", - "last_name": "Hartinger" - }, - "updated_at": "2018-12-05T08:19:36.641Z", - "issue_count": 0, - "system_count": 0, - "needs_reboot": true - } - ], - "meta": { - "count": 0, - "total": 0 - }, - "links": { - "first": null, - "last": null, - "next": null, - "previous": null - } -} diff --git a/awx/main/tests/data/inventory/plugins/insights/env.json b/awx/main/tests/data/inventory/plugins/insights/env.json new file mode 100644 index 0000000000..46eb0a34e7 --- /dev/null +++ b/awx/main/tests/data/inventory/plugins/insights/env.json @@ -0,0 +1,5 @@ +{ + "ANSIBLE_TRANSFORM_INVALID_GROUP_CHARS": "never", + "INSIGHTS_USER": "fooo", + "INSIGHTS_PASSWORD": "fooo" +} \ No newline at end of file diff --git a/awx/main/tests/functional/api/test_credential.py b/awx/main/tests/functional/api/test_credential.py index bf6e908c52..9fe6328633 100644 --- a/awx/main/tests/functional/api/test_credential.py +++ b/awx/main/tests/functional/api/test_credential.py @@ -652,6 +652,31 @@ def test_satellite6_create_ok(post, organization, admin): assert decrypt_field(cred, 'password') == 'some_password' +# +# RH Insights Credentials +# +@pytest.mark.django_db +def test_insights_create_ok(post, organization, admin): + params = { + 'credential_type': 1, + 'name': 'Best credential ever', + 'inputs': { + 'username': 'some_username', + 'password': 'some_password', + }, + } + sat6 = CredentialType.defaults['insights']() + sat6.save() + params['organization'] = organization.id + response = post(reverse('api:credential_list'), params, admin) + assert response.status_code == 201 + + assert Credential.objects.count() == 1 + cred = Credential.objects.all()[:1].get() + assert cred.inputs['username'] == 'some_username' + assert decrypt_field(cred, 'password') == 'some_password' + + # # AWS Credentials # diff --git a/awx/main/tests/functional/api/test_host_insights.py b/awx/main/tests/functional/api/test_host_insights.py deleted file mode 100644 index e5b3d8a783..0000000000 --- a/awx/main/tests/functional/api/test_host_insights.py +++ /dev/null @@ -1,145 +0,0 @@ -from collections import namedtuple - -import pytest -import requests - -from awx.api.versioning import reverse - - -@pytest.mark.django_db -class TestHostInsights: - def test_insights_bad_host(self, get, hosts, user, mocker): - mocker.patch.object(requests.Session, 'get') - - host = hosts(host_count=1)[0] - - url = reverse('api:host_insights', kwargs={'pk': host.pk}) - response = get(url, user('admin', True)) - - assert response.data['error'] == 'This host is not recognized as an Insights host.' - assert response.status_code == 404 - - def test_insights_host_missing_from_insights(self, get, hosts, insights_credential, user, mocker): - class Response: - status_code = 200 - content = "{'results': []}" - - def json(self): - return {'results': []} - - mocker.patch.object(requests.Session, 'get', return_value=Response()) - - host = hosts(host_count=1)[0] - host.insights_system_id = '123e4567-e89b-12d3-a456-426655440000' - host.inventory.insights_credential = insights_credential - host.inventory.save() - host.save() - - url = reverse('api:host_insights', kwargs={'pk': host.pk}) - response = get(url, user('admin', True)) - - assert response.data['error'] == ('Could not translate Insights system ID 123e4567-e89b-12d3-a456-426655440000' ' into an Insights platform ID.') - assert response.status_code == 404 - - def test_insights_no_credential(self, get, hosts, user, mocker): - mocker.patch.object(requests.Session, 'get') - - host = hosts(host_count=1)[0] - host.insights_system_id = '123e4567-e89b-12d3-a456-426655440000' - host.save() - - url = reverse('api:host_insights', kwargs={'pk': host.pk}) - response = get(url, user('admin', True)) - - assert response.data['error'] == 'The Insights Credential for "test-inv" was not found.' - assert response.status_code == 404 - - @pytest.mark.parametrize( - "status_code, exception, error, message", - [ - ( - 502, - requests.exceptions.SSLError, - 'SSLError while trying to connect to https://myexample.com/whocares/me/', - None, - ), - ( - 504, - requests.exceptions.Timeout, - 'Request to https://myexample.com/whocares/me/ timed out.', - None, - ), - (502, requests.exceptions.RequestException, 'booo!', 'Unknown exception booo! while trying to GET https://myexample.com/whocares/me/'), - ], - ) - def test_insights_exception(self, get, hosts, insights_credential, user, mocker, status_code, exception, error, message): - mocker.patch.object(requests.Session, 'get', side_effect=exception(error)) - - host = hosts(host_count=1)[0] - host.insights_system_id = '123e4567-e89b-12d3-a456-426655440000' - host.inventory.insights_credential = insights_credential - host.inventory.save() - host.save() - - url = reverse('api:host_insights', kwargs={'pk': host.pk}) - response = get(url, user('admin', True)) - - assert response.data['error'] == message or error - assert response.status_code == status_code - - def test_insights_unauthorized(self, get, hosts, insights_credential, user, mocker): - Response = namedtuple('Response', 'status_code content') - mocker.patch.object(requests.Session, 'get', return_value=Response(401, 'mock 401 err msg')) - - host = hosts(host_count=1)[0] - host.insights_system_id = '123e4567-e89b-12d3-a456-426655440000' - host.inventory.insights_credential = insights_credential - host.inventory.save() - host.save() - - url = reverse('api:host_insights', kwargs={'pk': host.pk}) - response = get(url, user('admin', True)) - - assert response.data['error'] == ("Unauthorized access. Please check your Insights Credential username and password.") - assert response.status_code == 502 - - def test_insights_bad_status(self, get, hosts, insights_credential, user, mocker): - Response = namedtuple('Response', 'status_code content') - mocker.patch.object(requests.Session, 'get', return_value=Response(500, 'mock 500 err msg')) - - host = hosts(host_count=1)[0] - host.insights_system_id = '123e4567-e89b-12d3-a456-426655440000' - host.inventory.insights_credential = insights_credential - host.inventory.save() - host.save() - - url = reverse('api:host_insights', kwargs={'pk': host.pk}) - response = get(url, user('admin', True)) - - assert response.data['error'].startswith("Failed to access the Insights API at URL") - assert "Server responded with 500 status code and message mock 500 err msg" in response.data['error'] - assert response.status_code == 502 - - def test_insights_bad_json(self, get, hosts, insights_credential, user, mocker): - class Response: - status_code = 200 - content = 'booo!' - - def json(self): - raise ValueError("we do not care what this is") - - mocker.patch.object(requests.Session, 'get', return_value=Response()) - - host = hosts(host_count=1)[0] - host.insights_system_id = '123e4567-e89b-12d3-a456-426655440000' - host.inventory.insights_credential = insights_credential - host.inventory.save() - host.save() - - url = reverse('api:host_insights', kwargs={'pk': host.pk}) - response = get(url, user('admin', True)) - - assert response.data['error'].startswith("Expected JSON response from Insights at URL") - assert 'insights_id=123e4567-e89b-12d3-a456-426655440000' in response.data['error'] - assert response.data['error'].endswith("but instead got booo!") - assert response.status_code == 502 diff --git a/awx/main/tests/functional/models/test_inventory.py b/awx/main/tests/functional/models/test_inventory.py index eab7e02895..c7303367b3 100644 --- a/awx/main/tests/functional/models/test_inventory.py +++ b/awx/main/tests/functional/models/test_inventory.py @@ -209,6 +209,7 @@ class TestInventorySourceInjectors: ('vmware', 'community.vmware.vmware_vm_inventory'), ('rhv', 'ovirt.ovirt.ovirt'), ('satellite6', 'theforeman.foreman.foreman'), + ('insights', 'redhatinsights.insights.insights'), ('tower', 'awx.awx.tower'), ], ) diff --git a/awx/main/tests/unit/models/test_jobs.py b/awx/main/tests/unit/models/test_jobs.py index d78a96305e..b9e803ec65 100644 --- a/awx/main/tests/unit/models/test_jobs.py +++ b/awx/main/tests/unit/models/test_jobs.py @@ -71,7 +71,7 @@ def test_finish_job_fact_cache_with_existing_data(job, hosts, inventory, mocker, for h in hosts: h.save = mocker.Mock() - ansible_facts_new = {"foo": "bar", "insights": {"system_id": "updated_by_scan"}} + ansible_facts_new = {"foo": "bar"} filepath = os.path.join(fact_cache, hosts[1].name) with open(filepath, 'w') as f: f.write(json.dumps(ansible_facts_new)) @@ -90,31 +90,9 @@ def test_finish_job_fact_cache_with_existing_data(job, hosts, inventory, mocker, assert host.ansible_facts == {"a": 1, "b": 2} assert host.ansible_facts_modified is None assert hosts[1].ansible_facts == ansible_facts_new - assert hosts[1].insights_system_id == "updated_by_scan" hosts[1].save.assert_called_once_with() -def test_finish_job_fact_cache_with_malformed_fact(job, hosts, inventory, mocker, tmpdir): - fact_cache = os.path.join(tmpdir, 'facts') - modified_times = {} - job.start_job_fact_cache(fact_cache, modified_times, 0) - - for h in hosts: - h.save = mocker.Mock() - - for h in hosts: - filepath = os.path.join(fact_cache, h.name) - with open(filepath, 'w') as f: - json.dump({'ansible_local': {'insights': 'this is an unexpected error from ansible'}}, f) - new_modification_time = time.time() + 3600 - os.utime(filepath, (new_modification_time, new_modification_time)) - - job.finish_job_fact_cache(fact_cache, modified_times) - - for h in hosts: - assert h.insights_system_id is None - - def test_finish_job_fact_cache_with_bad_data(job, hosts, inventory, mocker, tmpdir): fact_cache = os.path.join(tmpdir, 'facts') modified_times = {} diff --git a/awx/main/tests/unit/test_tasks.py b/awx/main/tests/unit/test_tasks.py index ec6c9d1bee..fe563e39cf 100644 --- a/awx/main/tests/unit/test_tasks.py +++ b/awx/main/tests/unit/test_tasks.py @@ -1746,6 +1746,34 @@ class TestInventoryUpdateCredentials(TestJobExecution): assert env["FOREMAN_PASSWORD"] == "secret" assert safe_env["FOREMAN_PASSWORD"] == tasks.HIDDEN_PASSWORD + def test_insights_source(self, inventory_update, private_data_dir, mocker): + task = tasks.RunInventoryUpdate() + task.instance = inventory_update + insights = CredentialType.defaults['insights']() + inventory_update.source = 'insights' + + def get_cred(): + cred = Credential( + pk=1, + credential_type=insights, + inputs={ + 'username': 'bob', + 'password': 'secret', + }, + ) + cred.inputs['password'] = encrypt_field(cred, 'password') + return cred + + inventory_update.get_cloud_credential = get_cred + inventory_update.get_extra_credentials = mocker.Mock(return_value=[]) + + env = task.build_env(inventory_update, private_data_dir, False) + safe_env = build_safe_env(env) + + assert env["INSIGHTS_USER"] == "bob" + assert env["INSIGHTS_PASSWORD"] == "secret" + assert safe_env['INSIGHTS_PASSWORD'] == tasks.HIDDEN_PASSWORD + @pytest.mark.parametrize('verify', [True, False]) def test_tower_source(self, verify, inventory_update, private_data_dir, mocker): task = tasks.RunInventoryUpdate() diff --git a/awx/main/tests/unit/utils/test_insights.py b/awx/main/tests/unit/utils/test_insights.py deleted file mode 100644 index b5dbe63d19..0000000000 --- a/awx/main/tests/unit/utils/test_insights.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (c) 2017 Ansible Tower by Red Hat -# All Rights Reserved. - - -from awx.main.utils.insights import filter_insights_api_response -from awx.main.tests.data.insights import TEST_INSIGHTS_HOSTS, TEST_INSIGHTS_PLANS, TEST_INSIGHTS_REMEDIATIONS - - -def test_filter_insights_api_response(): - actual = filter_insights_api_response(TEST_INSIGHTS_HOSTS['results'][0], TEST_INSIGHTS_PLANS, TEST_INSIGHTS_REMEDIATIONS) - - assert actual['last_check_in'] == '2019-03-19T21:59:09.213151-04:00' - assert len(actual['reports']) == 5 - assert len(actual['reports'][0]['maintenance_actions']) == 1 - assert actual['reports'][0]['maintenance_actions'][0]['name'] == "Fix Critical CVEs" - rule = actual['reports'][0]['rule'] - - assert rule['severity'] == 'WARN' - assert rule['description'] == ("Kernel vulnerable to side-channel attacks in modern microprocessors (CVE-2017-5715/Spectre)") - assert rule['category'] == 'Security' - assert rule['summary'] == ( - "A vulnerability was discovered in modern microprocessors supported by the kernel," - " whereby an unprivileged attacker can use this flaw to bypass restrictions to gain read" - " access to privileged memory.\nThe issue was reported as [CVE-2017-5715 / Spectre]" - "(https://access.redhat.com/security/cve/CVE-2017-5715).\n" - ) diff --git a/awx/main/utils/insights.py b/awx/main/utils/insights.py deleted file mode 100644 index 67c5a6e097..0000000000 --- a/awx/main/utils/insights.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (c) 2017 Ansible Tower by Red Hat -# All Rights Reserved. - - -# Old Insights API -> New API -# -# last_check_in is missing entirely, is now provided by a different endpoint -# reports[] -> [] -# reports[].rule.{description,summary} -> [].rule.{description,summary} -# reports[].rule.category -> [].rule.category.name -# reports[].rule.severity (str) -> [].rule.total_risk (int) - -# reports[].rule.{ansible,ansible_fix} appears to be unused -# reports[].maintenance_actions[] missing entirely, is now provided -# by a different Insights endpoint - - -def filter_insights_api_response(platform_info, reports, remediations): - severity_mapping = {1: 'INFO', 2: 'WARN', 3: 'ERROR', 4: 'CRITICAL'} - - new_json = { - 'platform_id': platform_info['id'], - 'last_check_in': platform_info.get('updated'), - 'reports': [], - } - for rep in reports: - new_report = {'rule': {}, 'maintenance_actions': remediations} - rule = rep.get('rule') or {} - for k in ['description', 'summary']: - if k in rule: - new_report['rule'][k] = rule[k] - if 'category' in rule: - new_report['rule']['category'] = rule['category']['name'] - if rule.get('total_risk') in severity_mapping: - new_report['rule']['severity'] = severity_mapping[rule['total_risk']] - - new_json['reports'].append(new_report) - - return new_json diff --git a/awx/settings/defaults.py b/awx/settings/defaults.py index ea47b43907..d1b38a2c1e 100644 --- a/awx/settings/defaults.py +++ b/awx/settings/defaults.py @@ -693,6 +693,14 @@ SATELLITE6_EXCLUDE_EMPTY_GROUPS = True SATELLITE6_INSTANCE_ID_VAR = 'foreman_id' # SATELLITE6_GROUP_PREFIX and SATELLITE6_GROUP_PATTERNS defined in source vars +# ---------------- +# -- Red Hat Insights -- +# ---------------- +# INSIGHTS_ENABLED_VAR = +# INSIGHTS_ENABLED_VALUE = +INSIGHTS_INSTANCE_ID_VAR = 'insights_id' +INSIGHTS_EXCLUDE_EMPTY_GROUPS = False + # --------------------- # ----- Custom ----- # --------------------- diff --git a/awx/ui_next/src/screens/Inventory/shared/InventorySourceForm.jsx b/awx/ui_next/src/screens/Inventory/shared/InventorySourceForm.jsx index a6b637a44a..aa3fc78266 100644 --- a/awx/ui_next/src/screens/Inventory/shared/InventorySourceForm.jsx +++ b/awx/ui_next/src/screens/Inventory/shared/InventorySourceForm.jsx @@ -20,6 +20,7 @@ import { AzureSubForm, EC2SubForm, GCESubForm, + InsightsSubForm, OpenStackSubForm, SCMSubForm, SatelliteSubForm, @@ -179,6 +180,13 @@ const InventorySourceFormFields = ({ sourceOptions={sourceOptions} /> ), + insights: ( + + ), openstack: ( { + const { setFieldValue, setFieldTouched } = useFormikContext(); + const [credentialField, credentialMeta, credentialHelpers] = useField( + 'credential' + ); + const config = useConfig(); + + const handleCredentialUpdate = useCallback( + value => { + setFieldValue('credential', value); + setFieldTouched('credential', true, false); + }, + [setFieldValue, setFieldTouched] + ); + + const pluginLink = `${getDocsBaseUrl( + config + )}/html/userguide/inventories.html#inventory-plugins`; + const configLink = + 'https://docs.ansible.com/ansible/latest/collections/redhatinsights/insights/insights_inventory.html'; + + return ( + <> + credentialHelpers.setTouched()} + onChange={handleCredentialUpdate} + value={credentialField.value} + required + autoPopulate={autoPopulateCredential} + validate={required(t`Select a value for this field`)} + /> + + + + + + + + Enter variables to configure the inventory source. For a detailed + description of how to configure this plugin, see{' '} + + Inventory Plugins + {' '} + in the documentation and the{' '} + + Insights + {' '} + plugin configuration guide. + +
+
+ + } + /> + + ); +}; + +export default InsightsSubForm; diff --git a/awx/ui_next/src/screens/Inventory/shared/InventorySourceSubForms/InsightsSubForm.test.jsx b/awx/ui_next/src/screens/Inventory/shared/InventorySourceSubForms/InsightsSubForm.test.jsx new file mode 100644 index 0000000000..9db84e617f --- /dev/null +++ b/awx/ui_next/src/screens/Inventory/shared/InventorySourceSubForms/InsightsSubForm.test.jsx @@ -0,0 +1,65 @@ +import React from 'react'; +import { act } from 'react-dom/test-utils'; +import { Formik } from 'formik'; +import { mountWithContexts } from '../../../../../testUtils/enzymeHelpers'; +import InsightsSubForm from './InsightsSubForm'; +import { CredentialsAPI } from '../../../../api'; + +jest.mock('../../../../api'); + +const initialValues = { + credential: null, + overwrite: false, + overwrite_vars: false, + source_path: '', + source_project: null, + source_script: null, + source_vars: '---\n', + update_cache_timeout: 0, + update_on_launch: true, + update_on_project_update: false, + verbosity: 1, +}; + +describe('', () => { + let wrapper; + + beforeEach(async () => { + CredentialsAPI.read.mockResolvedValue({ + data: { count: 0, results: [] }, + }); + await act(async () => { + wrapper = mountWithContexts( + + + + ); + }); + }); + + afterAll(() => { + jest.clearAllMocks(); + }); + + test('should render subform fields', () => { + expect(wrapper.find('FormGroup[label="Credential"]')).toHaveLength(1); + expect(wrapper.find('FormGroup[label="Verbosity"]')).toHaveLength(1); + expect(wrapper.find('FormGroup[label="Update options"]')).toHaveLength(1); + expect( + wrapper.find('FormGroup[label="Cache timeout (seconds)"]') + ).toHaveLength(1); + expect( + wrapper.find('VariablesField[label="Source variables"]') + ).toHaveLength(1); + }); + + test('should make expected api calls', () => { + expect(CredentialsAPI.read).toHaveBeenCalledTimes(1); + expect(CredentialsAPI.read).toHaveBeenCalledWith({ + credential_type__namespace: 'insights', + order_by: 'name', + page: 1, + page_size: 5, + }); + }); +}); diff --git a/awx/ui_next/src/screens/Inventory/shared/InventorySourceSubForms/index.js b/awx/ui_next/src/screens/Inventory/shared/InventorySourceSubForms/index.js index b7e8c15971..2efe263db7 100644 --- a/awx/ui_next/src/screens/Inventory/shared/InventorySourceSubForms/index.js +++ b/awx/ui_next/src/screens/Inventory/shared/InventorySourceSubForms/index.js @@ -1,6 +1,7 @@ export { default as AzureSubForm } from './AzureSubForm'; export { default as EC2SubForm } from './EC2SubForm'; export { default as GCESubForm } from './GCESubForm'; +export { default as InsightsSubForm } from './InsightsSubForm'; export { default as OpenStackSubForm } from './OpenStackSubForm'; export { default as SCMSubForm } from './SCMSubForm'; export { default as SatelliteSubForm } from './SatelliteSubForm';