def _get_sql_db_tde_disabled_event(com, ext): """Generate SQL DB disabled TDE event. Arguments: com (dict): SQL DB record `com` bucket ext (dict): SQL DB record `ext` bucket Returns: dict: An event record representing SQL DB with disabled TDE """ friendly_cloud_type = util.friendly_string(com.get('cloud_type')) reference = com.get('reference') description = ('{} SQL DB {} has TDE disabled.'.format( friendly_cloud_type, reference)) recommendation = ('Check {} SQL DB {} and enable TDE.'.format( friendly_cloud_type, reference)) event_record = { # Preserve the extended properties from the virtual # machine record because they provide useful context to # locate the virtual machine that led to the event. 'ext': util.merge_dicts(ext, {'record_type': 'sql_db_tde_event'}), 'com': { 'cloud_type': com.get('cloud_type'), 'record_type': 'sql_db_tde_event', 'reference': reference, 'description': description, 'recommendation': recommendation, } } _log.info('Generating sql_db_tde_event; %r', event_record) yield event_record
def _get_log_profile_missing_event(com, ext): """Create an event record for missing log profile. Arguments: com (dict): The `com` bucket of a ``log_profile_missing`` record. ext (dict): The `ext` bucket of a ``log_profile_missing`` record. Returns: dict: An event record representing log profile missing for a subscription. """ friendly_cloud_type = util.friendly_string(com.get('cloud_type')) reference = com.get('reference') description = ('{} subscription {} has log profile missing.'.format( friendly_cloud_type, reference)) recommendation = ( 'Check {} subscription {} and create a log profile.'.format( friendly_cloud_type, reference)) event_record = { # Preserve the extended properties from the virtual # machine record because they provide useful context to # locate the virtual machine that led to the event. 'ext': util.merge_dicts(ext, {'record_type': 'log_profile_event'}), 'com': { 'cloud_type': com.get('cloud_type'), 'record_type': 'log_profile_event', 'reference': reference, 'description': description, 'recommendation': recommendation, } } _log.info('Generating log_profile_event; %r', event_record) yield event_record
def _get_postgres_log_retention_days_event(com, ext, min_log_retention_days): """Generate event for Postgres log retention days set below minimum. Arguments: com (dict): Postgres record `com` bucket ext (dict): Postgres record `ext` bucket min_log_retention_days (int): Minimum log retention days Returns: dict: An event record representing Postgres server with log retention days set below desired minimum. """ friendly_cloud_type = util.friendly_string(com.get('cloud_type')) friendly_rdbms_type = util.friendly_string(ext.get('record_type')) reference = com.get('reference') description = ( '{} {} {} has log retention days set below desired minimum value.'. format(friendly_cloud_type, friendly_rdbms_type, reference)) recommendation = ( 'Check {} {} {} and set log retention days to minimum of {} days.'. format(friendly_cloud_type, friendly_rdbms_type, reference, min_log_retention_days)) event_record = { # Preserve the extended properties from the RDBMS # record because they provide useful context to # locate the RDBMS that led to the event. 'ext': util.merge_dicts(ext, {'record_type': 'postgres_log_retention_days_event'}), 'com': { 'cloud_type': com.get('cloud_type'), 'record_type': 'postgres_log_retention_days_event', 'reference': reference, 'description': description, 'recommendation': recommendation, } } _log.info('Generating postgres_log_retention_days_event; %r', event_record) yield event_record
def _get_postgres_log_disconnections_disabled_event(com, ext): """Generate event for Postgres log disconnections disabled. Arguments: com (dict): Postgres record `com` bucket ext (dict): Postgres record `ext` bucket Returns: dict: An event record representing Postgres server with log disconnections disabled """ friendly_cloud_type = util.friendly_string(com.get('cloud_type')) friendly_rdbms_type = util.friendly_string(ext.get('record_type')) reference = com.get('reference') description = ( '{} {} {} has log disconnections disabled.' .format(friendly_cloud_type, friendly_rdbms_type, reference) ) recommendation = ( 'Check {} {} {} and enable log disconnections.' .format(friendly_cloud_type, friendly_rdbms_type, reference) ) event_record = { # Preserve the extended properties from the RDBMS # record because they provide useful context to # locate the RDBMS that led to the event. 'ext': util.merge_dicts(ext, { 'record_type': 'postgres_log_disconnections_event' }), 'com': { 'cloud_type': com.get('cloud_type'), 'record_type': 'postgres_log_disconnections_event', 'reference': reference, 'description': description, 'recommendation': recommendation, } } _log.info('Generating postgres_log_disconnections_event; %r', event_record) yield event_record
def _get_azure_vm_data_disk_encryption_event(com, ext, raw): """Evaluate Azure VM for unencrypted data disks. Arguments: com (dict): Virtual machine record `com` bucket ext (dict): Virtual machine record `ext` bucket raw (dict): Virtual machine record `raw` bucket Returns: dict: An event record representing unencrypted data disk """ friendly_cloud_type = util.friendly_string(com.get('cloud_type')) os_disk_name = raw.get('storage_profile').get('os_disk').get('name') instance_view = raw.get('instance_view') if instance_view is None: return for disk in instance_view.get('disks'): # If it is an OS disk skip and continue if disk.get('name') == os_disk_name: continue if disk.get('encryption_settings') is not None and \ disk['encryption_settings'][0]['enabled']: continue reference = com.get('reference') description = ( '{} virtual machine {} has unencrypted data disk {}' .format(friendly_cloud_type, reference, disk.get('name')) ) recommendation = ( 'Check {} virtual machine {} and encrypt data disk {}' .format(friendly_cloud_type, reference, disk.get('name')) ) event_record = { # Preserve the extended properties from the virtual # machine record because they provide useful context to # locate the virtual machine that led to the event. 'ext': util.merge_dicts(ext, { 'record_type': 'vm_data_disk_encryption_event' }), 'com': { 'cloud_type': com.get('cloud_type'), 'record_type': 'vm_data_disk_encryption_event', 'reference': reference, 'description': description, 'recommendation': recommendation, } } _log.info('Generating vm_data_disk_encryption_event; %r', event_record) yield event_record
def _get_az_storage_account_secure_transfer_event(com, ext): """Evaluate Azure storage account to check if insecure transfer enabled. Arguments: com (dict): Azure storage account record `com` bucket. ext (dict): Azure storage account record `ext` bucket. Returns: dict: An event record representing storage accounts with secure transfer enabled set to false. """ friendly_cloud_type = util.friendly_string(com.get('cloud_type')) reference = com.get('reference') description = ( '{} storage account {} does not require secure transfer.' .format(friendly_cloud_type, reference) ) recommendation = ( 'Check {} storage account {} and ensure that it requires ' 'secure transfer.'.format(friendly_cloud_type, reference) ) event_record = { # Preserve the properties from the storage account # record because they provide useful context to # locate the storage account that led to the event. 'ext': util.merge_dicts(ext, { 'record_type': 'storage_account_secure_transfer_event' }), 'com': { 'cloud_type': com.get('cloud_type'), 'record_type': 'storage_account_secure_transfer_event', 'reference': reference, 'description': description, 'recommendation': recommendation, } } _log.info('Generating storage_account_secure_transfer_event; %r', event_record) yield event_record
def _get_az_storage_account_default_network_access_event(com, ext): """Generate Azure storage account default network access event. Arguments: com (dict): Azure storage account record `com` bucket. ext (dict): Azure storage account record `ext` bucket. Returns: dict: An event record representing storage accounts with default network access set to allowed. """ friendly_cloud_type = util.friendly_string(com.get('cloud_type')) reference = com.get('reference') description = ( '{} storage account {} has default network access set to allowed.'. format(friendly_cloud_type, reference)) recommendation = ( 'Check {} storage account {} and set default network access to deny.'. format(friendly_cloud_type, reference)) event_record = { # Preserve the properties from the storage account # record because they provide useful context to # locate the storage account that led to the event. 'ext': util.merge_dicts( ext, {'record_type': 'storage_account_default_network_access_event'}), 'com': { 'cloud_type': com.get('cloud_type'), 'record_type': 'storage_account_default_network_access_event', 'reference': reference, 'description': description, 'recommendation': recommendation, } } _log.info('Generating storage_account_default_network_access_event; %r', event_record) yield event_record
def _get_log_profile_missing_location_event(com, ext, missing_locations): """Generate log profile missing category type event. Arguments: com (dict): Log profile record `com` bucket ext (dict): Log profile record `ext` bucket missing_locations (set): Missing location set Returns: dict: An event record representing log profile which is not enabled for all locations. """ friendly_cloud_type = util.friendly_string(com.get('cloud_type')) reference = com.get('reference') description = ('{} log profile {} does not include locations {}.'.format( friendly_cloud_type, reference, util.friendly_list(missing_locations))) recommendation = ( 'Check {} log profile {} and enable locations {}.'.format( friendly_cloud_type, reference, util.friendly_list(missing_locations))) event_record = { # Preserve the extended properties from the log profile # record because they provide useful context to locate # the log profile that led to the event. 'ext': util.merge_dicts( ext, { 'record_type': 'log_profile_missing_location_event', 'missing_locations': missing_locations }), 'com': { 'cloud_type': com.get('cloud_type'), 'record_type': 'log_profile_missing_location_event', 'reference': reference, 'description': description, 'recommendation': recommendation, } } _log.info('Generating log_profile_missing_location_event; %r', event_record) return event_record
def _get_azure_vm_blacklisted_extension_event(com, ext, blacklisted): """Evaluate Azure VM for blacklisted extensions. Arguments: com (dict): Virtual machine record `com` bucket ext (dict): Virtual machine record `ext` bucket blacklisted (list): Added blacklisted extension list Returns: dict: An event record representing VM with blacklisted extenstions """ if not blacklisted: return friendly_cloud_type = util.friendly_string(com.get('cloud_type')) reference = com.get('reference') description = ( '{} virtual machine {} has blacklisted extensions {}'.format( friendly_cloud_type, reference, util.friendly_list(blacklisted))) recommendation = ( 'Check {} virtual machine {} and remove blacklisted extensions {}'. format(friendly_cloud_type, reference, util.friendly_list(blacklisted))) event_record = { # Preserve the extended properties from the virtual # machine record because they provide useful context to # locate the virtual machine that led to the event. 'ext': util.merge_dicts(ext, {'record_type': 'vm_blacklisted_extension_event'}), 'com': { 'cloud_type': com.get('cloud_type'), 'record_type': 'vm_blacklisted_extension_event', 'reference': reference, 'description': description, 'recommendation': recommendation, } } _log.info('Generating vm_blacklisted_extension_event; %r', event_record) yield event_record
def _get_azure_web_app_https_event(com, ext): """Generate Web App HTTPS event. Arguments: com (dict): Azure web app record `com` bucket. ext (dict): Azure web app record `ext` bucket. Returns: dict: An event record representing web apps not using HTTPS only traffic. """ friendly_cloud_type = util.friendly_string(com.get('cloud_type')) reference = com.get('reference') description = ( '{} web app {} has HTTPS only traffic disabled.' .format(friendly_cloud_type, reference) ) recommendation = ( 'Check {} web app {} and enable HTTPS only traffic.' .format(friendly_cloud_type, reference) ) event_record = { # Preserve the extended properties from the web app # record because they provide useful context to # locate the web app that led to the event. 'ext': util.merge_dicts(ext, { 'record_type': 'web_app_https_event' }), 'com': { 'cloud_type': com.get('cloud_type'), 'record_type': 'web_app_https_event', 'reference': reference, 'description': description, 'recommendation': recommendation, } } _log.info('Generating web_app_https_event; %r', event_record) yield event_record
def _get_azure_vm_os_disk_encryption_event(com, ext, raw): """Evaluate Azure VM for unencrypted OS disks. Arguments: com (dict): Virtual machine record `com` bucket ext (dict): Virtual machine record `ext` bucket raw (dict): Virtual machine record `raw` bucket Returns: dict: An event record representing unencrypted OS disk """ friendly_cloud_type = util.friendly_string(com.get('cloud_type')) os_disk_name = raw.get('storage_profile').get('os_disk').get('name') reference = com.get('reference') description = ( '{} virtual machine {} has unencrypted OS disk {}' .format(friendly_cloud_type, reference, os_disk_name) ) recommendation = ( 'Check {} virtual machine {} and encrypt OS disk {}' .format(friendly_cloud_type, reference, os_disk_name) ) event_record = { # Preserve the extended properties from the virtual # machine record because they provide useful context to # locate the virtual machine that led to the event. 'ext': util.merge_dicts(ext, { 'record_type': 'vm_os_disk_encryption_event' }), 'com': { 'cloud_type': com.get('cloud_type'), 'record_type': 'vm_os_disk_encryption_event', 'reference': reference, 'description': description, 'recommendation': recommendation, } } _log.info('Generating vm_os_disk_encryption_event; %r', event_record) yield event_record
def _get_log_profile_retention_event(com, ext, min_retention_days): """Generate log profile retention event. Arguments: com (dict): Log profile record `com` bucket ext (dict): Log profile record `ext` bucket min_retention_days (int): Minimum required retention days. Returns: dict: An event record representing log profile with retention policy configured for less number of days than required. """ friendly_cloud_type = util.friendly_string(com.get('cloud_type')) reference = com.get('reference') description = ( '{} log profile {} has log retention set to less than {} days.'.format( friendly_cloud_type, reference, min_retention_days)) recommendation = ( 'Check {} log profile {} and set log retention to more than {} days.'. format(friendly_cloud_type, reference, min_retention_days)) event_record = { # Preserve the extended properties from the virtual # machine record because they provide useful context to # locate the virtual machine that led to the event. 'ext': util.merge_dicts(ext, {'record_type': 'log_profile_retention_event'}), 'com': { 'cloud_type': com.get('cloud_type'), 'record_type': 'log_profile_retention_event', 'reference': reference, 'description': description, 'recommendation': recommendation, } } _log.info('Generating log_profile_retention_event; %r', event_record) yield event_record
def _get_key_vault_secret_no_expiry_event(com, ext): """Generate Key Vault secret expiry event. Arguments: com (dict): Key Vault secret record `com` bucket. ext (dict): Key Vault secret record `ext` bucket. Returns: dict: An event record representing Key Vault secret with no expiry set. """ friendly_cloud_type = util.friendly_string(com.get('cloud_type')) reference = com.get('reference') description = ( '{} Key Vault secret {} has has no expiration date set.'.format( friendly_cloud_type, reference)) recommendation = ( 'Check {} Key Vault secret {} and set expiration date.'.format( friendly_cloud_type, reference)) event_record = { # Preserve the extended properties from the key vault # secret record because they provide useful context to # locate the key vault secret that led to the event. 'ext': util.merge_dicts(ext, {'record_type': 'key_vault_secret_no_expiry_event'}), 'com': { 'cloud_type': com.get('cloud_type'), 'record_type': 'key_vault_secret_no_expiry_event', 'reference': reference, 'description': description, 'recommendation': recommendation, } } _log.info('Generating key_vault_secret_no_expiry_event; %r', event_record) yield event_record
def _get_azure_web_app_tls_event(com, ext, min_tls_version): """Evaluate Azure web app config for insecure min TLS version. Arguments: com (dict): Azure web app record `com` bucket ext (dict): Azure web app record `ext` bucket min_tls_version (float): Minimum required TLS version Returns: dict: An event record representing web apps not using a minimum TLS version """ friendly_cloud_type = util.friendly_string(com.get('cloud_type')) reference = com.get('reference') description = ('{} web app {} has insecure minimum TLS version.'.format( friendly_cloud_type, reference)) recommendation = ( 'Check {} web app {} and ensure the minimum TLS version is set to {}.'. format(friendly_cloud_type, reference, str(min_tls_version))) event_record = { # Preserve the extended properties from the web app # record because they provide useful context to # locate the web app that led to the event. 'ext': util.merge_dicts(ext, {'record_type': 'web_app_tls_event'}), 'com': { 'cloud_type': com.get('cloud_type'), 'record_type': 'web_app_tls_event', 'reference': reference, 'description': description, 'recommendation': recommendation, } } _log.info('Generating web_app_tls_event; %r', event_record) yield event_record
def eval(self, record): """Evaluate firewall rules to check for insecurely exposed ports. Arguments: record (dict): A firewall rule record. Yields: dict: An event record representing an insecurely exposed port. """ # If 'com' bucket is missing, we have a malformed record. Log a # warning and ignore it. com = record.get('com') if com is None: _log.warning('Firewall rule record is missing com key: %r', record) return # This plugin understands firewall rule records only, so ignore # any other record types. common_record_type = com.get('record_type') if common_record_type != 'firewall_rule': return # Ignore disabled firewall rule. if not com.get('enabled'): return # If the rule is not an ingress/inbound rule, ignore it. if com.get('direction') != 'in': return # If the rule is not an allow rule, ignore it. if com.get('access') != 'allow': return # If the rule is not a TCP port rule, ignore it. if com.get('protocol') not in ('tcp', 'all'): return # If the rule does not expose ports to the entire Internet, # ignore it. if '0.0.0.0/0' not in com.get('source_addresses'): return # Find the set of ports in self._ports that are exposed by the # firewall rule record. port_ranges = com.get('destination_ports') expanded_ports = util.expand_port_ranges(port_ranges) exposed_ports = self._ports.intersection(expanded_ports) # If there are no insecurely exposed ports, we do not need to # generate an event. if exposed_ports == set(): return # Convert the set of ports to a sorted list of ports. exposed_ports = sorted(list(exposed_ports)) # Human-friendly plain English description of the event along # with a recommendation. friendly_cloud_type = util.friendly_string(com.get('cloud_type')) port_label = util.pluralize(len(exposed_ports), 'port') friendly_exposed_ports = util.friendly_list(exposed_ports) reference = com.get('reference') description = ( '{} firewall rule {} exposes {} {} to the entire Internet.'.format( friendly_cloud_type, reference, port_label, friendly_exposed_ports)) recommendation = ( 'Check {} firewall rule {} and update rules to restrict ' 'access to {} {}.'.format(friendly_cloud_type, reference, port_label, friendly_exposed_ports)) event_record = { # Preserve the extended properties from the firewall # rule record because they provide useful context to # locate the firewall rule that led to the event. 'ext': record.get('ext', {}), 'com': { 'cloud_type': com.get('cloud_type'), 'record_type': 'firewall_rule_event', 'exposed_ports': exposed_ports, 'reference': reference, 'description': description, 'recommendation': recommendation, } } # Set the extended record type. event_record['ext']['record_type'] = 'firewall_rule_event' _log.info('Generating firewall_rule_event; %r', event_record) yield event_record
def test_friendly_string_missing(self): s = util.friendly_string('foo') self.assertEqual(s, 'foo')
def test_friendly_string_present(self): s = util.friendly_string('azure') self.assertEqual(s, 'Azure')