prowler 5.17.1__py3-none-any.whl → 5.18.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dashboard/compliance/hipaa_azure.py +25 -0
- dashboard/pages/overview.py +20 -11
- prowler/AGENTS.md +1 -1
- prowler/CHANGELOG.md +43 -0
- prowler/__main__.py +5 -0
- prowler/compliance/azure/hipaa_azure.json +820 -0
- prowler/compliance/m365/cis_4.0_m365.json +6 -2
- prowler/compliance/m365/cis_6.0_m365.json +6 -2
- prowler/compliance/m365/iso27001_2022_m365.json +13 -11
- prowler/compliance/openstack/__init__.py +0 -0
- prowler/config/config.py +2 -1
- prowler/config/config.yaml +4 -1
- prowler/config/openstack_mutelist_example.yaml +60 -0
- prowler/lib/check/check.py +4 -0
- prowler/lib/check/models.py +27 -2
- prowler/lib/cli/parser.py +3 -2
- prowler/lib/outputs/finding.py +14 -0
- prowler/lib/outputs/html/html.py +72 -0
- prowler/lib/outputs/jira/jira.py +3 -3
- prowler/lib/outputs/outputs.py +2 -0
- prowler/lib/outputs/summary_table.py +7 -0
- prowler/lib/timeline/__init__.py +0 -0
- prowler/lib/timeline/models.py +27 -0
- prowler/lib/timeline/timeline.py +36 -0
- prowler/providers/aws/lib/cloudtrail_timeline/__init__.py +0 -0
- prowler/providers/aws/lib/cloudtrail_timeline/cloudtrail_timeline.py +218 -0
- prowler/providers/aws/services/codebuild/codebuild_project_webhook_filters_use_anchored_patterns/__init__.py +0 -0
- prowler/providers/aws/services/codebuild/codebuild_project_webhook_filters_use_anchored_patterns/codebuild_project_webhook_filters_use_anchored_patterns.metadata.json +40 -0
- prowler/providers/aws/services/codebuild/codebuild_project_webhook_filters_use_anchored_patterns/codebuild_project_webhook_filters_use_anchored_patterns.py +58 -0
- prowler/providers/aws/services/codebuild/codebuild_service.py +45 -0
- prowler/providers/aws/services/dynamodb/dynamodb_table_cross_account_access/dynamodb_table_cross_account_access.metadata.json +1 -1
- prowler/providers/aws/services/dynamodb/dynamodb_table_cross_account_access/dynamodb_table_cross_account_access.py +4 -0
- prowler/providers/aws/services/eventbridge/eventbridge_bus_cross_account_access/eventbridge_bus_cross_account_access.metadata.json +1 -1
- prowler/providers/aws/services/eventbridge/eventbridge_bus_cross_account_access/eventbridge_bus_cross_account_access.py +4 -0
- prowler/providers/aws/services/eventbridge/eventbridge_schema_registry_cross_account_access/eventbridge_schema_registry_cross_account_access.metadata.json +1 -1
- prowler/providers/aws/services/eventbridge/eventbridge_schema_registry_cross_account_access/eventbridge_schema_registry_cross_account_access.py +2 -0
- prowler/providers/aws/services/iam/lib/policy.py +19 -3
- prowler/providers/aws/services/rds/rds_instance_extended_support/__init__.py +0 -0
- prowler/providers/aws/services/rds/rds_instance_extended_support/rds_instance_extended_support.metadata.json +41 -0
- prowler/providers/aws/services/rds/rds_instance_extended_support/rds_instance_extended_support.py +37 -0
- prowler/providers/aws/services/rds/rds_service.py +4 -0
- prowler/providers/aws/services/s3/s3_bucket_cross_account_access/s3_bucket_cross_account_access.metadata.json +1 -1
- prowler/providers/aws/services/s3/s3_bucket_cross_account_access/s3_bucket_cross_account_access.py +5 -1
- prowler/providers/azure/lib/service/service.py +23 -0
- prowler/providers/azure/services/app/app_client_certificates_on/app_client_certificates_on.metadata.json +18 -12
- prowler/providers/azure/services/app/app_ensure_auth_is_set_up/app_ensure_auth_is_set_up.metadata.json +18 -11
- prowler/providers/azure/services/app/app_ensure_http_is_redirected_to_https/app_ensure_http_is_redirected_to_https.metadata.json +19 -12
- prowler/providers/azure/services/app/app_ensure_java_version_is_latest/app_ensure_java_version_is_latest.metadata.json +19 -12
- prowler/providers/azure/services/app/app_ensure_php_version_is_latest/app_ensure_php_version_is_latest.metadata.json +19 -12
- prowler/providers/azure/services/app/app_ensure_python_version_is_latest/app_ensure_python_version_is_latest.metadata.json +19 -12
- prowler/providers/azure/services/app/app_ensure_using_http20/app_ensure_using_http20.metadata.json +18 -11
- prowler/providers/azure/services/app/app_ftp_deployment_disabled/app_ftp_deployment_disabled.metadata.json +21 -13
- prowler/providers/azure/services/app/app_function_access_keys_configured/app_function_access_keys_configured.metadata.json +19 -11
- prowler/providers/azure/services/app/app_function_application_insights_enabled/app_function_application_insights_enabled.metadata.json +21 -14
- prowler/providers/azure/services/app/app_function_ftps_deployment_disabled/app_function_ftps_deployment_disabled.metadata.json +18 -13
- prowler/providers/azure/services/app/app_function_identity_is_configured/app_function_identity_is_configured.metadata.json +20 -13
- prowler/providers/azure/services/app/app_function_identity_without_admin_privileges/app_function_identity_without_admin_privileges.metadata.json +18 -11
- prowler/providers/azure/services/app/app_function_latest_runtime_version/app_function_latest_runtime_version.metadata.json +20 -13
- prowler/providers/azure/services/app/app_function_not_publicly_accessible/app_function_not_publicly_accessible.metadata.json +20 -13
- prowler/providers/azure/services/app/app_function_vnet_integration_enabled/app_function_vnet_integration_enabled.metadata.json +21 -14
- prowler/providers/azure/services/app/app_http_logs_enabled/app_http_logs_enabled.metadata.json +18 -12
- prowler/providers/azure/services/app/app_minimum_tls_version_12/app_minimum_tls_version_12.metadata.json +20 -12
- prowler/providers/azure/services/app/app_register_with_identity/app_register_with_identity.metadata.json +18 -11
- prowler/providers/azure/services/appinsights/appinsights_ensure_is_configured/appinsights_ensure_is_configured.metadata.json +18 -12
- prowler/providers/azure/services/containerregistry/containerregistry_admin_user_disabled/containerregistry_admin_user_disabled.metadata.json +17 -11
- prowler/providers/azure/services/containerregistry/containerregistry_not_publicly_accessible/containerregistry_not_publicly_accessible.metadata.json +18 -12
- prowler/providers/azure/services/containerregistry/containerregistry_uses_private_link/containerregistry_uses_private_link.metadata.json +21 -13
- prowler/providers/azure/services/cosmosdb/cosmosdb_account_firewall_use_selected_networks/cosmosdb_account_firewall_use_selected_networks.metadata.json +20 -12
- prowler/providers/azure/services/cosmosdb/cosmosdb_account_use_aad_and_rbac/cosmosdb_account_use_aad_and_rbac.metadata.json +19 -13
- prowler/providers/azure/services/cosmosdb/cosmosdb_account_use_private_endpoints/cosmosdb_account_use_private_endpoints.metadata.json +20 -13
- prowler/providers/azure/services/databricks/databricks_workspace_cmk_encryption_enabled/databricks_workspace_cmk_encryption_enabled.metadata.json +20 -14
- prowler/providers/azure/services/databricks/databricks_workspace_vnet_injection_enabled/databricks_workspace_vnet_injection_enabled.metadata.json +20 -14
- prowler/providers/azure/services/defender/defender_additional_email_configured_with_a_security_contact/defender_additional_email_configured_with_a_security_contact.metadata.json +20 -13
- prowler/providers/azure/services/defender/defender_assessments_vm_endpoint_protection_installed/defender_assessments_vm_endpoint_protection_installed.metadata.json +17 -11
- prowler/providers/azure/services/defender/defender_attack_path_notifications_properly_configured/defender_attack_path_notifications_properly_configured.metadata.json +19 -13
- prowler/providers/azure/services/defender/defender_auto_provisioning_log_analytics_agent_vms_on/defender_auto_provisioning_log_analytics_agent_vms_on.metadata.json +20 -13
- prowler/providers/azure/services/defender/defender_auto_provisioning_vulnerabilty_assessments_machines_on/defender_auto_provisioning_vulnerabilty_assessments_machines_on.metadata.json +19 -12
- prowler/providers/azure/services/defender/defender_container_images_resolved_vulnerabilities/defender_container_images_resolved_vulnerabilities.metadata.json +20 -12
- prowler/providers/azure/services/defender/defender_container_images_scan_enabled/defender_container_images_scan_enabled.metadata.json +22 -13
- prowler/providers/azure/services/defender/defender_ensure_defender_for_app_services_is_on/defender_ensure_defender_for_app_services_is_on.metadata.json +17 -11
- prowler/providers/azure/services/defender/defender_ensure_defender_for_arm_is_on/defender_ensure_defender_for_arm_is_on.metadata.json +17 -11
- prowler/providers/azure/services/defender/defender_ensure_defender_for_azure_sql_databases_is_on/defender_ensure_defender_for_azure_sql_databases_is_on.metadata.json +17 -11
- prowler/providers/azure/services/defender/defender_ensure_defender_for_containers_is_on/defender_ensure_defender_for_containers_is_on.metadata.json +17 -11
- prowler/providers/azure/services/defender/defender_ensure_defender_for_cosmosdb_is_on/defender_ensure_defender_for_cosmosdb_is_on.metadata.json +17 -11
- prowler/providers/azure/services/defender/defender_ensure_defender_for_databases_is_on/defender_ensure_defender_for_databases_is_on.metadata.json +17 -11
- prowler/providers/azure/services/defender/defender_ensure_defender_for_dns_is_on/defender_ensure_defender_for_dns_is_on.metadata.json +17 -11
- prowler/providers/azure/services/defender/defender_ensure_defender_for_keyvault_is_on/defender_ensure_defender_for_keyvault_is_on.metadata.json +17 -11
- prowler/providers/azure/services/defender/defender_ensure_defender_for_os_relational_databases_is_on/defender_ensure_defender_for_os_relational_databases_is_on.metadata.json +17 -11
- prowler/providers/azure/services/defender/defender_ensure_defender_for_server_is_on/defender_ensure_defender_for_server_is_on.metadata.json +19 -11
- prowler/providers/azure/services/defender/defender_ensure_defender_for_sql_servers_is_on/defender_ensure_defender_for_sql_servers_is_on.metadata.json +17 -11
- prowler/providers/azure/services/defender/defender_ensure_defender_for_storage_is_on/defender_ensure_defender_for_storage_is_on.metadata.json +17 -11
- prowler/providers/azure/services/defender/defender_ensure_iot_hub_defender_is_on/defender_ensure_iot_hub_defender_is_on.metadata.json +17 -11
- prowler/providers/azure/services/defender/defender_ensure_mcas_is_enabled/defender_ensure_mcas_is_enabled.metadata.json +20 -12
- prowler/providers/azure/services/defender/defender_ensure_notify_alerts_severity_is_high/defender_ensure_notify_alerts_severity_is_high.metadata.json +19 -12
- prowler/providers/azure/services/defender/defender_ensure_notify_emails_to_owners/defender_ensure_notify_emails_to_owners.metadata.json +19 -12
- prowler/providers/azure/services/defender/defender_ensure_system_updates_are_applied/defender_ensure_system_updates_are_applied.metadata.json +17 -9
- prowler/providers/azure/services/defender/defender_ensure_wdatp_is_enabled/defender_ensure_wdatp_is_enabled.metadata.json +21 -13
- prowler/providers/azure/services/entra/entra_service.py +3 -11
- prowler/providers/azure/services/entra/entra_user_with_vm_access_has_mfa/entra_user_with_vm_access_has_mfa.py +6 -0
- prowler/providers/azure/services/iam/iam_custom_role_has_permissions_to_administer_resource_locks/iam_custom_role_has_permissions_to_administer_resource_locks.metadata.json +19 -13
- prowler/providers/azure/services/iam/iam_role_user_access_admin_restricted/iam_role_user_access_admin_restricted.metadata.json +16 -10
- prowler/providers/azure/services/iam/iam_subscription_roles_owner_custom_not_created/iam_subscription_roles_owner_custom_not_created.metadata.json +18 -12
- prowler/providers/azure/services/keyvault/keyvault_rbac_secret_expiration_set/keyvault_rbac_secret_expiration_set.py +10 -11
- prowler/providers/azure/services/keyvault/keyvault_service.py +164 -81
- prowler/providers/azure/services/mysql/mysql_flexible_server_audit_log_connection_activated/mysql_flexible_server_audit_log_connection_activated.metadata.json +18 -12
- prowler/providers/azure/services/mysql/mysql_flexible_server_audit_log_enabled/mysql_flexible_server_audit_log_enabled.metadata.json +19 -12
- prowler/providers/azure/services/mysql/mysql_flexible_server_minimum_tls_version_12/mysql_flexible_server_minimum_tls_version_12.metadata.json +18 -12
- prowler/providers/azure/services/mysql/mysql_flexible_server_ssl_connection_enabled/mysql_flexible_server_ssl_connection_enabled.metadata.json +19 -12
- prowler/providers/azure/services/network/network_bastion_host_exists/network_bastion_host_exists.metadata.json +21 -12
- prowler/providers/azure/services/network/network_flow_log_captured_sent/network_flow_log_captured_sent.metadata.json +19 -12
- prowler/providers/azure/services/network/network_flow_log_more_than_90_days/network_flow_log_more_than_90_days.metadata.json +21 -12
- prowler/providers/azure/services/network/network_http_internet_access_restricted/network_http_internet_access_restricted.metadata.json +18 -12
- prowler/providers/azure/services/network/network_public_ip_shodan/network_public_ip_shodan.metadata.json +15 -10
- prowler/providers/azure/services/network/network_rdp_internet_access_restricted/network_rdp_internet_access_restricted.metadata.json +20 -12
- prowler/providers/azure/services/network/network_ssh_internet_access_restricted/network_ssh_internet_access_restricted.metadata.json +19 -12
- prowler/providers/azure/services/network/network_udp_internet_access_restricted/network_udp_internet_access_restricted.metadata.json +19 -12
- prowler/providers/azure/services/network/network_watcher_enabled/network_watcher_enabled.metadata.json +21 -13
- prowler/providers/azure/services/policy/policy_ensure_asc_enforcement_enabled/policy_ensure_asc_enforcement_enabled.metadata.json +16 -11
- prowler/providers/azure/services/postgresql/postgresql_flexible_server_allow_access_services_disabled/postgresql_flexible_server_allow_access_services_disabled.metadata.json +20 -13
- prowler/providers/azure/services/postgresql/postgresql_flexible_server_connection_throttling_on/postgresql_flexible_server_connection_throttling_on.metadata.json +18 -12
- prowler/providers/azure/services/postgresql/postgresql_flexible_server_enforce_ssl_enabled/postgresql_flexible_server_enforce_ssl_enabled.metadata.json +19 -13
- prowler/providers/azure/services/postgresql/postgresql_flexible_server_entra_id_authentication_enabled/postgresql_flexible_server_entra_id_authentication_enabled.metadata.json +4 -4
- prowler/providers/azure/services/postgresql/postgresql_flexible_server_log_checkpoints_on/postgresql_flexible_server_log_checkpoints_on.metadata.json +19 -13
- prowler/providers/azure/services/postgresql/postgresql_flexible_server_log_connections_on/postgresql_flexible_server_log_connections_on.metadata.json +18 -11
- prowler/providers/azure/services/postgresql/postgresql_flexible_server_log_disconnections_on/postgresql_flexible_server_log_disconnections_on.metadata.json +18 -12
- prowler/providers/azure/services/postgresql/postgresql_flexible_server_log_retention_days_greater_3/postgresql_flexible_server_log_retention_days_greater_3.metadata.json +18 -12
- prowler/providers/azure/services/sqlserver/sqlserver_auditing_enabled/sqlserver_auditing_enabled.metadata.json +20 -13
- prowler/providers/azure/services/sqlserver/sqlserver_auditing_retention_90_days/sqlserver_auditing_retention_90_days.metadata.json +20 -12
- prowler/providers/azure/services/sqlserver/sqlserver_azuread_administrator_enabled/sqlserver_azuread_administrator_enabled.metadata.json +18 -12
- prowler/providers/azure/services/sqlserver/sqlserver_microsoft_defender_enabled/sqlserver_microsoft_defender_enabled.metadata.json +23 -13
- prowler/providers/azure/services/sqlserver/sqlserver_recommended_minimal_tls_version/sqlserver_recommended_minimal_tls_version.metadata.json +19 -12
- prowler/providers/azure/services/sqlserver/sqlserver_tde_encrypted_with_cmk/sqlserver_tde_encrypted_with_cmk.metadata.json +20 -13
- prowler/providers/azure/services/sqlserver/sqlserver_tde_encryption_enabled/sqlserver_tde_encryption_enabled.metadata.json +20 -13
- prowler/providers/azure/services/sqlserver/sqlserver_unrestricted_inbound_access/sqlserver_unrestricted_inbound_access.metadata.json +18 -12
- prowler/providers/azure/services/sqlserver/sqlserver_va_emails_notifications_admins_enabled/sqlserver_va_emails_notifications_admins_enabled.metadata.json +19 -12
- prowler/providers/azure/services/sqlserver/sqlserver_va_periodic_recurring_scans_enabled/sqlserver_va_periodic_recurring_scans_enabled.metadata.json +19 -12
- prowler/providers/azure/services/sqlserver/sqlserver_va_scan_reports_configured/sqlserver_va_scan_reports_configured.metadata.json +18 -12
- prowler/providers/azure/services/sqlserver/sqlserver_vulnerability_assessment_enabled/sqlserver_vulnerability_assessment_enabled.metadata.json +19 -12
- prowler/providers/azure/services/storage/storage_account_key_access_disabled/storage_account_key_access_disabled.metadata.json +17 -12
- prowler/providers/azure/services/storage/storage_blob_public_access_level_is_disabled/storage_blob_public_access_level_is_disabled.metadata.json +18 -12
- prowler/providers/azure/services/storage/storage_blob_versioning_is_enabled/storage_blob_versioning_is_enabled.metadata.json +19 -11
- prowler/providers/azure/services/storage/storage_cross_tenant_replication_disabled/storage_cross_tenant_replication_disabled.metadata.json +19 -13
- prowler/providers/azure/services/storage/storage_default_network_access_rule_is_denied/storage_default_network_access_rule_is_denied.metadata.json +19 -12
- prowler/providers/azure/services/storage/storage_default_to_entra_authorization_enabled/storage_default_to_entra_authorization_enabled.metadata.json +20 -13
- prowler/providers/azure/services/storage/storage_ensure_azure_services_are_trusted_to_access_is_enabled/storage_ensure_azure_services_are_trusted_to_access_is_enabled.metadata.json +17 -10
- prowler/providers/azure/services/storage/storage_ensure_encryption_with_customer_managed_keys/storage_ensure_encryption_with_customer_managed_keys.metadata.json +15 -10
- prowler/providers/azure/services/storage/storage_ensure_file_shares_soft_delete_is_enabled/storage_ensure_file_shares_soft_delete_is_enabled.metadata.json +18 -12
- prowler/providers/azure/services/storage/storage_ensure_minimum_tls_version_12/storage_ensure_minimum_tls_version_12.metadata.json +14 -10
- prowler/providers/azure/services/storage/storage_ensure_private_endpoints_in_storage_accounts/storage_ensure_private_endpoints_in_storage_accounts.metadata.json +19 -11
- prowler/providers/azure/services/storage/storage_ensure_soft_delete_is_enabled/storage_ensure_soft_delete_is_enabled.metadata.json +17 -12
- prowler/providers/azure/services/storage/storage_geo_redundant_enabled/storage_geo_redundant_enabled.metadata.json +19 -12
- prowler/providers/azure/services/storage/storage_infrastructure_encryption_is_enabled/storage_infrastructure_encryption_is_enabled.metadata.json +13 -9
- prowler/providers/azure/services/storage/storage_key_rotation_90_days/storage_key_rotation_90_days.metadata.json +17 -12
- prowler/providers/azure/services/storage/storage_secure_transfer_required_is_enabled/storage_secure_transfer_required_is_enabled.metadata.json +15 -11
- prowler/providers/azure/services/storage/storage_smb_channel_encryption_with_secure_algorithm/storage_smb_channel_encryption_with_secure_algorithm.metadata.json +19 -12
- prowler/providers/azure/services/storage/storage_smb_protocol_version_is_latest/storage_smb_protocol_version_is_latest.metadata.json +19 -13
- prowler/providers/cloudflare/cloudflare_provider.py +95 -12
- prowler/providers/cloudflare/lib/arguments/arguments.py +7 -0
- prowler/providers/cloudflare/services/dns/dns_record_cname_target_valid/__init__.py +0 -0
- prowler/providers/cloudflare/services/dns/dns_record_cname_target_valid/dns_record_cname_target_valid.metadata.json +36 -0
- prowler/providers/cloudflare/services/dns/dns_record_cname_target_valid/dns_record_cname_target_valid.py +109 -0
- prowler/providers/cloudflare/services/dns/dns_record_no_internal_ip/__init__.py +0 -0
- prowler/providers/cloudflare/services/dns/dns_record_no_internal_ip/dns_record_no_internal_ip.metadata.json +36 -0
- prowler/providers/cloudflare/services/dns/dns_record_no_internal_ip/dns_record_no_internal_ip.py +73 -0
- prowler/providers/cloudflare/services/dns/dns_record_no_wildcard/__init__.py +0 -0
- prowler/providers/cloudflare/services/dns/dns_record_no_wildcard/dns_record_no_wildcard.metadata.json +36 -0
- prowler/providers/cloudflare/services/dns/dns_record_no_wildcard/dns_record_no_wildcard.py +60 -0
- prowler/providers/cloudflare/services/dns/dns_record_proxied/__init__.py +0 -0
- prowler/providers/cloudflare/services/dns/dns_record_proxied/dns_record_proxied.metadata.json +36 -0
- prowler/providers/cloudflare/services/dns/dns_record_proxied/dns_record_proxied.py +49 -0
- prowler/providers/cloudflare/services/dns/dns_service.py +52 -6
- prowler/providers/cloudflare/services/firewall/__init__.py +0 -0
- prowler/providers/cloudflare/services/firewall/firewall_client.py +4 -0
- prowler/providers/cloudflare/services/firewall/firewall_service.py +123 -0
- prowler/providers/cloudflare/services/zone/zone_firewall_blocking_rules_configured/__init__.py +0 -0
- prowler/providers/cloudflare/services/zone/zone_firewall_blocking_rules_configured/zone_firewall_blocking_rules_configured.metadata.json +36 -0
- prowler/providers/cloudflare/services/zone/zone_firewall_blocking_rules_configured/zone_firewall_blocking_rules_configured.py +53 -0
- prowler/providers/cloudflare/services/zone/zone_service.py +133 -1
- prowler/providers/cloudflare/services/zone/zone_waf_owasp_ruleset_enabled/__init__.py +0 -0
- prowler/providers/cloudflare/services/zone/zone_waf_owasp_ruleset_enabled/zone_waf_owasp_ruleset_enabled.metadata.json +36 -0
- prowler/providers/cloudflare/services/zone/zone_waf_owasp_ruleset_enabled/zone_waf_owasp_ruleset_enabled.py +58 -0
- prowler/providers/common/provider.py +23 -0
- prowler/providers/gcp/services/compute/compute_instance_suspended_without_persistent_disks/__init__.py +0 -0
- prowler/providers/gcp/services/compute/compute_instance_suspended_without_persistent_disks/compute_instance_suspended_without_persistent_disks.metadata.json +37 -0
- prowler/providers/gcp/services/compute/compute_instance_suspended_without_persistent_disks/compute_instance_suspended_without_persistent_disks.py +35 -0
- prowler/providers/gcp/services/compute/compute_service.py +2 -0
- prowler/providers/m365/lib/powershell/m365_powershell.py +47 -1
- prowler/providers/m365/services/defender/defender_service.py +52 -0
- prowler/providers/m365/services/defender/defender_zap_for_teams_enabled/__init__.py +0 -0
- prowler/providers/m365/services/defender/defender_zap_for_teams_enabled/defender_zap_for_teams_enabled.metadata.json +38 -0
- prowler/providers/m365/services/defender/defender_zap_for_teams_enabled/defender_zap_for_teams_enabled.py +53 -0
- prowler/providers/m365/services/exchange/exchange_service.py +78 -0
- prowler/providers/m365/services/exchange/exchange_shared_mailbox_sign_in_disabled/__init__.py +0 -0
- prowler/providers/m365/services/exchange/exchange_shared_mailbox_sign_in_disabled/exchange_shared_mailbox_sign_in_disabled.metadata.json +37 -0
- prowler/providers/m365/services/exchange/exchange_shared_mailbox_sign_in_disabled/exchange_shared_mailbox_sign_in_disabled.py +59 -0
- prowler/providers/openstack/__init__.py +0 -0
- prowler/providers/openstack/exceptions/__init__.py +0 -0
- prowler/providers/openstack/exceptions/exceptions.py +166 -0
- prowler/providers/openstack/lib/__init__.py +0 -0
- prowler/providers/openstack/lib/arguments/__init__.py +0 -0
- prowler/providers/openstack/lib/arguments/arguments.py +113 -0
- prowler/providers/openstack/lib/mutelist/__init__.py +0 -0
- prowler/providers/openstack/lib/mutelist/mutelist.py +31 -0
- prowler/providers/openstack/lib/service/__init__.py +0 -0
- prowler/providers/openstack/lib/service/service.py +21 -0
- prowler/providers/openstack/models.py +100 -0
- prowler/providers/openstack/openstack_provider.py +515 -0
- prowler/providers/openstack/services/__init__.py +0 -0
- prowler/providers/openstack/services/compute/__init__.py +0 -0
- prowler/providers/openstack/services/compute/compute_client.py +4 -0
- prowler/providers/openstack/services/compute/compute_instance_security_groups_attached/__init__.py +0 -0
- prowler/providers/openstack/services/compute/compute_instance_security_groups_attached/compute_instance_security_groups_attached.metadata.json +40 -0
- prowler/providers/openstack/services/compute/compute_instance_security_groups_attached/compute_instance_security_groups_attached.py +35 -0
- prowler/providers/openstack/services/compute/compute_service.py +63 -0
- {prowler-5.17.1.dist-info → prowler-5.18.0.dist-info}/METADATA +11 -9
- {prowler-5.17.1.dist-info → prowler-5.18.0.dist-info}/RECORD +219 -155
- {prowler-5.17.1.dist-info → prowler-5.18.0.dist-info}/LICENSE +0 -0
- {prowler-5.17.1.dist-info → prowler-5.18.0.dist-info}/WHEEL +0 -0
- {prowler-5.17.1.dist-info → prowler-5.18.0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
from os import environ
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from colorama import Fore, Style
|
|
6
|
+
from openstack import config, connect
|
|
7
|
+
from openstack import exceptions as openstack_exceptions
|
|
8
|
+
from openstack.connection import Connection as OpenStackConnection
|
|
9
|
+
|
|
10
|
+
from prowler.config.config import (
|
|
11
|
+
default_config_file_path,
|
|
12
|
+
get_default_mute_file_path,
|
|
13
|
+
load_and_validate_config_file,
|
|
14
|
+
)
|
|
15
|
+
from prowler.lib.logger import logger
|
|
16
|
+
from prowler.lib.utils.utils import print_boxes
|
|
17
|
+
from prowler.providers.common.models import Audit_Metadata, Connection
|
|
18
|
+
from prowler.providers.common.provider import Provider
|
|
19
|
+
from prowler.providers.openstack.exceptions.exceptions import (
|
|
20
|
+
OpenStackAuthenticationError,
|
|
21
|
+
OpenStackCloudNotFoundError,
|
|
22
|
+
OpenStackConfigFileNotFoundError,
|
|
23
|
+
OpenStackCredentialsError,
|
|
24
|
+
OpenStackInvalidConfigError,
|
|
25
|
+
OpenStackSessionError,
|
|
26
|
+
)
|
|
27
|
+
from prowler.providers.openstack.lib.mutelist.mutelist import OpenStackMutelist
|
|
28
|
+
from prowler.providers.openstack.models import OpenStackIdentityInfo, OpenStackSession
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class OpenstackProvider(Provider):
|
|
32
|
+
"""OpenStack provider responsible for bootstrapping the SDK session."""
|
|
33
|
+
|
|
34
|
+
_type: str = "openstack"
|
|
35
|
+
_session: OpenStackSession
|
|
36
|
+
_identity: OpenStackIdentityInfo
|
|
37
|
+
_audit_config: dict
|
|
38
|
+
_mutelist: OpenStackMutelist
|
|
39
|
+
_connection: OpenStackConnection
|
|
40
|
+
audit_metadata: Audit_Metadata
|
|
41
|
+
|
|
42
|
+
REQUIRED_ENVIRONMENT_VARIABLES = [
|
|
43
|
+
"OS_AUTH_URL",
|
|
44
|
+
"OS_USERNAME",
|
|
45
|
+
"OS_PASSWORD",
|
|
46
|
+
"OS_REGION_NAME",
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
def __init__(
|
|
50
|
+
self,
|
|
51
|
+
clouds_yaml_file: Optional[str] = None,
|
|
52
|
+
clouds_yaml_cloud: Optional[str] = None,
|
|
53
|
+
auth_url: Optional[str] = None,
|
|
54
|
+
identity_api_version: Optional[str] = None,
|
|
55
|
+
username: Optional[str] = None,
|
|
56
|
+
password: Optional[str] = None,
|
|
57
|
+
project_id: Optional[str] = None,
|
|
58
|
+
region_name: Optional[str] = None,
|
|
59
|
+
user_domain_name: Optional[str] = None,
|
|
60
|
+
project_domain_name: Optional[str] = None,
|
|
61
|
+
config_path: Optional[str] = None,
|
|
62
|
+
config_content: Optional[dict] = None,
|
|
63
|
+
fixer_config: Optional[dict] = None,
|
|
64
|
+
mutelist_path: Optional[str] = None,
|
|
65
|
+
mutelist_content: Optional[dict] = None,
|
|
66
|
+
) -> None:
|
|
67
|
+
logger.info("Instantiating OpenStack Provider...")
|
|
68
|
+
|
|
69
|
+
self._session = self.setup_session(
|
|
70
|
+
clouds_yaml_file=clouds_yaml_file,
|
|
71
|
+
clouds_yaml_cloud=clouds_yaml_cloud,
|
|
72
|
+
auth_url=auth_url,
|
|
73
|
+
identity_api_version=identity_api_version,
|
|
74
|
+
username=username,
|
|
75
|
+
password=password,
|
|
76
|
+
project_id=project_id,
|
|
77
|
+
region_name=region_name,
|
|
78
|
+
user_domain_name=user_domain_name,
|
|
79
|
+
project_domain_name=project_domain_name,
|
|
80
|
+
)
|
|
81
|
+
self._connection = OpenstackProvider._create_connection(self._session)
|
|
82
|
+
self._identity = OpenstackProvider.setup_identity(
|
|
83
|
+
self._connection, self._session
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
if config_content:
|
|
87
|
+
self._audit_config = config_content
|
|
88
|
+
else:
|
|
89
|
+
if not config_path:
|
|
90
|
+
config_path = default_config_file_path
|
|
91
|
+
self._audit_config = load_and_validate_config_file(self._type, config_path)
|
|
92
|
+
|
|
93
|
+
self._fixer_config = fixer_config or {}
|
|
94
|
+
|
|
95
|
+
if mutelist_content:
|
|
96
|
+
self._mutelist = OpenStackMutelist(mutelist_content=mutelist_content)
|
|
97
|
+
else:
|
|
98
|
+
if not mutelist_path:
|
|
99
|
+
mutelist_path = get_default_mute_file_path(self.type)
|
|
100
|
+
self._mutelist = OpenStackMutelist(mutelist_path=mutelist_path)
|
|
101
|
+
|
|
102
|
+
Provider.set_global_provider(self)
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def type(self) -> str:
|
|
106
|
+
return self._type
|
|
107
|
+
|
|
108
|
+
@property
|
|
109
|
+
def session(self) -> OpenStackSession:
|
|
110
|
+
return self._session
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def identity(self) -> OpenStackIdentityInfo:
|
|
114
|
+
return self._identity
|
|
115
|
+
|
|
116
|
+
@property
|
|
117
|
+
def audit_config(self) -> dict:
|
|
118
|
+
return self._audit_config
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
def fixer_config(self) -> dict:
|
|
122
|
+
return self._fixer_config
|
|
123
|
+
|
|
124
|
+
@property
|
|
125
|
+
def mutelist(self) -> OpenStackMutelist:
|
|
126
|
+
return self._mutelist
|
|
127
|
+
|
|
128
|
+
@property
|
|
129
|
+
def connection(self) -> OpenStackConnection:
|
|
130
|
+
return self._connection
|
|
131
|
+
|
|
132
|
+
@staticmethod
|
|
133
|
+
def setup_session(
|
|
134
|
+
clouds_yaml_file: Optional[str] = None,
|
|
135
|
+
clouds_yaml_cloud: Optional[str] = None,
|
|
136
|
+
auth_url: Optional[str] = None,
|
|
137
|
+
identity_api_version: Optional[str] = None,
|
|
138
|
+
username: Optional[str] = None,
|
|
139
|
+
password: Optional[str] = None,
|
|
140
|
+
project_id: Optional[str] = None,
|
|
141
|
+
region_name: Optional[str] = None,
|
|
142
|
+
user_domain_name: Optional[str] = None,
|
|
143
|
+
project_domain_name: Optional[str] = None,
|
|
144
|
+
) -> OpenStackSession:
|
|
145
|
+
"""Collect authentication information from clouds.yaml, explicit parameters, or environment variables.
|
|
146
|
+
|
|
147
|
+
Authentication priority:
|
|
148
|
+
1. clouds.yaml file (if clouds_yaml_file or clouds_yaml_cloud provided)
|
|
149
|
+
2. Explicit parameters + environment variable fallback
|
|
150
|
+
"""
|
|
151
|
+
# Priority 1: clouds.yaml authentication
|
|
152
|
+
if clouds_yaml_file or clouds_yaml_cloud:
|
|
153
|
+
logger.info("Using clouds.yaml configuration for authentication")
|
|
154
|
+
return OpenstackProvider._setup_session_from_clouds_yaml(
|
|
155
|
+
clouds_yaml_file=clouds_yaml_file,
|
|
156
|
+
clouds_yaml_cloud=clouds_yaml_cloud,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
# Priority 2: Explicit parameters + environment variable fallback (existing behavior)
|
|
160
|
+
provided_overrides = {
|
|
161
|
+
"OS_AUTH_URL": auth_url,
|
|
162
|
+
"OS_USERNAME": username,
|
|
163
|
+
"OS_PASSWORD": password,
|
|
164
|
+
"OS_REGION_NAME": region_name,
|
|
165
|
+
}
|
|
166
|
+
missing_variables = [
|
|
167
|
+
env_var
|
|
168
|
+
for env_var in OpenstackProvider.REQUIRED_ENVIRONMENT_VARIABLES
|
|
169
|
+
if not (provided_overrides.get(env_var) or environ.get(env_var))
|
|
170
|
+
]
|
|
171
|
+
|
|
172
|
+
# Resolve project_id from parameters or environment
|
|
173
|
+
resolved_project_id = project_id or environ.get("OS_PROJECT_ID")
|
|
174
|
+
|
|
175
|
+
# OS_PROJECT_ID is mandatory
|
|
176
|
+
if not resolved_project_id:
|
|
177
|
+
missing_variables.append("OS_PROJECT_ID")
|
|
178
|
+
|
|
179
|
+
if missing_variables:
|
|
180
|
+
pretty_missing = ", ".join(missing_variables)
|
|
181
|
+
raise OpenStackCredentialsError(
|
|
182
|
+
message=f"Missing mandatory OpenStack environment variables: {pretty_missing}"
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
resolved_identity_api_version = (
|
|
186
|
+
identity_api_version or environ.get("OS_IDENTITY_API_VERSION") or "3"
|
|
187
|
+
)
|
|
188
|
+
resolved_user_domain = (
|
|
189
|
+
user_domain_name or environ.get("OS_USER_DOMAIN_NAME") or "Default"
|
|
190
|
+
)
|
|
191
|
+
resolved_project_domain = (
|
|
192
|
+
project_domain_name or environ.get("OS_PROJECT_DOMAIN_NAME") or "Default"
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
return OpenStackSession(
|
|
196
|
+
auth_url=auth_url or environ.get("OS_AUTH_URL"),
|
|
197
|
+
identity_api_version=resolved_identity_api_version,
|
|
198
|
+
username=username or environ.get("OS_USERNAME"),
|
|
199
|
+
password=password or environ.get("OS_PASSWORD"),
|
|
200
|
+
project_id=resolved_project_id,
|
|
201
|
+
region_name=region_name or environ.get("OS_REGION_NAME"),
|
|
202
|
+
user_domain_name=resolved_user_domain,
|
|
203
|
+
project_domain_name=resolved_project_domain,
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
@staticmethod
|
|
207
|
+
def _setup_session_from_clouds_yaml(
|
|
208
|
+
clouds_yaml_file: Optional[str] = None,
|
|
209
|
+
clouds_yaml_cloud: Optional[str] = None,
|
|
210
|
+
) -> OpenStackSession:
|
|
211
|
+
"""Setup session from clouds.yaml configuration file.
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
clouds_yaml_file: Path to clouds.yaml file. If None, standard locations are searched.
|
|
215
|
+
clouds_yaml_cloud: Cloud name to use from clouds.yaml. Required when using clouds.yaml.
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
OpenStackSession configured from clouds.yaml
|
|
219
|
+
|
|
220
|
+
Raises:
|
|
221
|
+
OpenStackConfigFileNotFoundError: If clouds.yaml file not found
|
|
222
|
+
OpenStackCloudNotFoundError: If specified cloud not found in clouds.yaml
|
|
223
|
+
OpenStackInvalidConfigError: If clouds.yaml is malformed or missing required fields
|
|
224
|
+
"""
|
|
225
|
+
try:
|
|
226
|
+
# Cloud name is required when using clouds.yaml
|
|
227
|
+
if not clouds_yaml_cloud:
|
|
228
|
+
raise OpenStackInvalidConfigError(
|
|
229
|
+
file=clouds_yaml_file,
|
|
230
|
+
message="Cloud name (--clouds-yaml-cloud) is required when using clouds.yaml file",
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
# Determine config file path
|
|
234
|
+
if clouds_yaml_file:
|
|
235
|
+
# Use explicit path
|
|
236
|
+
config_path = Path(clouds_yaml_file).expanduser()
|
|
237
|
+
if not config_path.exists():
|
|
238
|
+
raise OpenStackConfigFileNotFoundError(
|
|
239
|
+
file=str(config_path),
|
|
240
|
+
message=f"clouds.yaml file not found at {config_path}",
|
|
241
|
+
)
|
|
242
|
+
logger.info(f"Loading clouds.yaml from {config_path}")
|
|
243
|
+
# Load OpenStack configuration with explicit file
|
|
244
|
+
os_config = config.OpenStackConfig(config_files=[str(config_path)])
|
|
245
|
+
else:
|
|
246
|
+
# Search standard locations if cloud name is provided
|
|
247
|
+
logger.info(
|
|
248
|
+
"Searching for clouds.yaml in standard locations: "
|
|
249
|
+
"~/.config/openstack/clouds.yaml, /etc/openstack/clouds.yaml, ./clouds.yaml"
|
|
250
|
+
)
|
|
251
|
+
# Load OpenStack configuration from standard locations (don't pass config_files)
|
|
252
|
+
os_config = config.OpenStackConfig()
|
|
253
|
+
|
|
254
|
+
# Get cloud configuration
|
|
255
|
+
logger.info(f"Loading cloud configuration for '{clouds_yaml_cloud}'")
|
|
256
|
+
|
|
257
|
+
try:
|
|
258
|
+
cloud_config = os_config.get_one(cloud=clouds_yaml_cloud)
|
|
259
|
+
except openstack_exceptions.OpenStackCloudException as error:
|
|
260
|
+
if "cloud" in str(error).lower() and "not found" in str(error).lower():
|
|
261
|
+
raise OpenStackCloudNotFoundError(
|
|
262
|
+
file=clouds_yaml_file,
|
|
263
|
+
original_exception=error,
|
|
264
|
+
message=f"Cloud '{clouds_yaml_cloud}' not found in clouds.yaml configuration",
|
|
265
|
+
)
|
|
266
|
+
raise OpenStackInvalidConfigError(
|
|
267
|
+
file=clouds_yaml_file,
|
|
268
|
+
original_exception=error,
|
|
269
|
+
message=f"Failed to load cloud configuration: {error}",
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
# Extract authentication parameters from cloud config
|
|
273
|
+
auth_dict = cloud_config.config.get("auth", {})
|
|
274
|
+
|
|
275
|
+
# Validate required fields
|
|
276
|
+
required_fields = ["auth_url", "username", "password"]
|
|
277
|
+
missing_fields = [
|
|
278
|
+
field for field in required_fields if not auth_dict.get(field)
|
|
279
|
+
]
|
|
280
|
+
if missing_fields:
|
|
281
|
+
raise OpenStackInvalidConfigError(
|
|
282
|
+
file=clouds_yaml_file,
|
|
283
|
+
message=f"Missing required fields in clouds.yaml for cloud '{clouds_yaml_cloud}': {', '.join(missing_fields)}",
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
# Build OpenStackSession from cloud config
|
|
287
|
+
return OpenStackSession(
|
|
288
|
+
auth_url=auth_dict.get("auth_url"),
|
|
289
|
+
identity_api_version=str(
|
|
290
|
+
cloud_config.config.get("identity_api_version", "3")
|
|
291
|
+
),
|
|
292
|
+
username=auth_dict.get("username"),
|
|
293
|
+
password=auth_dict.get("password"),
|
|
294
|
+
project_id=auth_dict.get("project_id") or auth_dict.get("project_name"),
|
|
295
|
+
region_name=cloud_config.config.get("region_name"),
|
|
296
|
+
user_domain_name=auth_dict.get("user_domain_name", "Default"),
|
|
297
|
+
project_domain_name=auth_dict.get("project_domain_name", "Default"),
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
except (
|
|
301
|
+
OpenStackConfigFileNotFoundError,
|
|
302
|
+
OpenStackCloudNotFoundError,
|
|
303
|
+
OpenStackInvalidConfigError,
|
|
304
|
+
):
|
|
305
|
+
# Re-raise our custom exceptions
|
|
306
|
+
raise
|
|
307
|
+
except Exception as error:
|
|
308
|
+
logger.critical(
|
|
309
|
+
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}] -- "
|
|
310
|
+
f"Failed to load clouds.yaml configuration: {error}"
|
|
311
|
+
)
|
|
312
|
+
raise OpenStackInvalidConfigError(
|
|
313
|
+
file=clouds_yaml_file,
|
|
314
|
+
original_exception=error,
|
|
315
|
+
message=f"Failed to load clouds.yaml configuration: {error}",
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
@staticmethod
|
|
319
|
+
def _create_connection(
|
|
320
|
+
session: OpenStackSession,
|
|
321
|
+
) -> OpenStackConnection:
|
|
322
|
+
"""Initialize the OpenStack SDK connection.
|
|
323
|
+
|
|
324
|
+
Note: We explicitly disable loading configuration from clouds.yaml
|
|
325
|
+
and environment variables to ensure Prowler uses only the credentials
|
|
326
|
+
provided through its own configuration mechanisms (CLI args, config file,
|
|
327
|
+
or environment variables read by Prowler itself in setup_session()).
|
|
328
|
+
"""
|
|
329
|
+
try:
|
|
330
|
+
# Don't load from clouds.yaml or environment variables, we configure this in setup_session()
|
|
331
|
+
conn = connect(
|
|
332
|
+
load_yaml_config=False,
|
|
333
|
+
load_envvars=False,
|
|
334
|
+
**session.as_sdk_config(),
|
|
335
|
+
)
|
|
336
|
+
conn.authorize()
|
|
337
|
+
return conn
|
|
338
|
+
except openstack_exceptions.SDKException as error:
|
|
339
|
+
logger.critical(
|
|
340
|
+
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}] -- "
|
|
341
|
+
f"Failed to create OpenStack connection: {error}"
|
|
342
|
+
)
|
|
343
|
+
raise OpenStackAuthenticationError(
|
|
344
|
+
original_exception=error,
|
|
345
|
+
message=f"Failed to create OpenStack connection: {error}",
|
|
346
|
+
)
|
|
347
|
+
except Exception as error:
|
|
348
|
+
logger.critical(
|
|
349
|
+
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}] -- "
|
|
350
|
+
f"Unexpected error while creating OpenStack connection: {error}"
|
|
351
|
+
)
|
|
352
|
+
raise OpenStackSessionError(
|
|
353
|
+
original_exception=error,
|
|
354
|
+
message=f"Unexpected error while creating OpenStack connection: {error}",
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
@staticmethod
|
|
358
|
+
def setup_identity(
|
|
359
|
+
conn: OpenStackConnection, session: OpenStackSession
|
|
360
|
+
) -> OpenStackIdentityInfo:
|
|
361
|
+
"""Build identity information for CLI/logging purposes."""
|
|
362
|
+
user_name = session.username
|
|
363
|
+
project_name = None
|
|
364
|
+
user_id = None
|
|
365
|
+
project_id = session.project_id
|
|
366
|
+
try:
|
|
367
|
+
user_id = conn.current_user_id
|
|
368
|
+
if user_id:
|
|
369
|
+
user = conn.identity.get_user(user_id)
|
|
370
|
+
if user and getattr(user, "name", None):
|
|
371
|
+
user_name = user.name
|
|
372
|
+
|
|
373
|
+
project_identifier = conn.current_project_id or session.project_id
|
|
374
|
+
if project_identifier:
|
|
375
|
+
project = conn.identity.get_project(project_identifier)
|
|
376
|
+
if project:
|
|
377
|
+
project_name = getattr(project, "name", None)
|
|
378
|
+
project_id = project_identifier
|
|
379
|
+
except openstack_exceptions.SDKException as error:
|
|
380
|
+
logger.warning(f"Unable to enrich OpenStack identity information: {error}")
|
|
381
|
+
except Exception as error:
|
|
382
|
+
logger.warning(f"Unexpected error building OpenStack identity: {error}")
|
|
383
|
+
|
|
384
|
+
return OpenStackIdentityInfo(
|
|
385
|
+
user_id=user_id,
|
|
386
|
+
username=user_name,
|
|
387
|
+
project_id=project_id,
|
|
388
|
+
project_name=project_name,
|
|
389
|
+
region_name=session.region_name,
|
|
390
|
+
user_domain_name=session.user_domain_name,
|
|
391
|
+
project_domain_name=session.project_domain_name,
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
@staticmethod
|
|
395
|
+
def test_connection(
|
|
396
|
+
clouds_yaml_file: Optional[str] = None,
|
|
397
|
+
clouds_yaml_cloud: Optional[str] = None,
|
|
398
|
+
auth_url: Optional[str] = None,
|
|
399
|
+
identity_api_version: Optional[str] = None,
|
|
400
|
+
username: Optional[str] = None,
|
|
401
|
+
password: Optional[str] = None,
|
|
402
|
+
project_id: Optional[str] = None,
|
|
403
|
+
region_name: Optional[str] = None,
|
|
404
|
+
user_domain_name: Optional[str] = None,
|
|
405
|
+
project_domain_name: Optional[str] = None,
|
|
406
|
+
raise_on_exception: bool = True,
|
|
407
|
+
) -> Connection:
|
|
408
|
+
"""Test connection to OpenStack without creating a full provider instance.
|
|
409
|
+
|
|
410
|
+
This static method allows testing OpenStack credentials without initializing
|
|
411
|
+
the entire provider. Useful for API validation before storing credentials.
|
|
412
|
+
|
|
413
|
+
Args:
|
|
414
|
+
clouds_yaml_file: Path to clouds.yaml configuration file
|
|
415
|
+
clouds_yaml_cloud: Cloud name from clouds.yaml to use
|
|
416
|
+
auth_url: OpenStack Keystone authentication URL
|
|
417
|
+
identity_api_version: Keystone API version (default: "3")
|
|
418
|
+
username: OpenStack username
|
|
419
|
+
password: OpenStack password
|
|
420
|
+
project_id: OpenStack project identifier (can be UUID or string ID)
|
|
421
|
+
region_name: OpenStack region name
|
|
422
|
+
user_domain_name: User domain name (default: "Default")
|
|
423
|
+
project_domain_name: Project domain name (default: "Default")
|
|
424
|
+
raise_on_exception: Whether to raise exception on failure (default: True)
|
|
425
|
+
|
|
426
|
+
Returns:
|
|
427
|
+
Connection object with is_connected=True on success, or error on failure
|
|
428
|
+
|
|
429
|
+
Raises:
|
|
430
|
+
OpenStackCredentialsError: If raise_on_exception=True and credentials are invalid
|
|
431
|
+
OpenStackAuthenticationError: If raise_on_exception=True and authentication fails
|
|
432
|
+
OpenStackSessionError: If raise_on_exception=True and connection fails
|
|
433
|
+
OpenStackConfigFileNotFoundError: If raise_on_exception=True and clouds.yaml not found
|
|
434
|
+
OpenStackCloudNotFoundError: If raise_on_exception=True and cloud not in clouds.yaml
|
|
435
|
+
OpenStackInvalidConfigError: If raise_on_exception=True and clouds.yaml is malformed
|
|
436
|
+
|
|
437
|
+
Examples:
|
|
438
|
+
>>> # Test with explicit credentials
|
|
439
|
+
>>> OpenstackProvider.test_connection(
|
|
440
|
+
... auth_url="https://openstack.example.com:5000/v3",
|
|
441
|
+
... username="admin",
|
|
442
|
+
... password="secret",
|
|
443
|
+
... project_id="my-project-id",
|
|
444
|
+
... region_name="RegionOne"
|
|
445
|
+
... )
|
|
446
|
+
Connection(is_connected=True, error=None)
|
|
447
|
+
|
|
448
|
+
>>> # Test with clouds.yaml
|
|
449
|
+
>>> OpenstackProvider.test_connection(
|
|
450
|
+
... clouds_yaml_file="~/.config/openstack/clouds.yaml",
|
|
451
|
+
... clouds_yaml_cloud="production"
|
|
452
|
+
... )
|
|
453
|
+
Connection(is_connected=True, error=None)
|
|
454
|
+
"""
|
|
455
|
+
try:
|
|
456
|
+
# Setup session with provided credentials
|
|
457
|
+
session = OpenstackProvider.setup_session(
|
|
458
|
+
clouds_yaml_file=clouds_yaml_file,
|
|
459
|
+
clouds_yaml_cloud=clouds_yaml_cloud,
|
|
460
|
+
auth_url=auth_url,
|
|
461
|
+
identity_api_version=identity_api_version,
|
|
462
|
+
username=username,
|
|
463
|
+
password=password,
|
|
464
|
+
project_id=project_id,
|
|
465
|
+
region_name=region_name,
|
|
466
|
+
user_domain_name=user_domain_name,
|
|
467
|
+
project_domain_name=project_domain_name,
|
|
468
|
+
)
|
|
469
|
+
|
|
470
|
+
# Create and test connection
|
|
471
|
+
OpenstackProvider._create_connection(session)
|
|
472
|
+
|
|
473
|
+
logger.info("OpenStack provider: Connection test successful")
|
|
474
|
+
return Connection(is_connected=True)
|
|
475
|
+
|
|
476
|
+
except (
|
|
477
|
+
OpenStackCredentialsError,
|
|
478
|
+
OpenStackAuthenticationError,
|
|
479
|
+
OpenStackSessionError,
|
|
480
|
+
OpenStackConfigFileNotFoundError,
|
|
481
|
+
OpenStackCloudNotFoundError,
|
|
482
|
+
OpenStackInvalidConfigError,
|
|
483
|
+
) as error:
|
|
484
|
+
logger.error(f"OpenStack connection test failed: {error}")
|
|
485
|
+
if raise_on_exception:
|
|
486
|
+
raise
|
|
487
|
+
return Connection(is_connected=False, error=error)
|
|
488
|
+
except Exception as error:
|
|
489
|
+
logger.error(
|
|
490
|
+
f"OpenStack connection test failed with unexpected error: {error}"
|
|
491
|
+
)
|
|
492
|
+
if raise_on_exception:
|
|
493
|
+
raise OpenStackSessionError(
|
|
494
|
+
original_exception=error,
|
|
495
|
+
message=f"Unexpected error during connection test: {error}",
|
|
496
|
+
)
|
|
497
|
+
return Connection(is_connected=False, error=error)
|
|
498
|
+
|
|
499
|
+
def print_credentials(self) -> None:
|
|
500
|
+
"""Output sanitized credential summary."""
|
|
501
|
+
region = self._session.region_name
|
|
502
|
+
auth_url = self._session.auth_url
|
|
503
|
+
project_id = self._session.project_id
|
|
504
|
+
username = self._identity.username
|
|
505
|
+
messages = [
|
|
506
|
+
f"Auth URL: {auth_url}",
|
|
507
|
+
f"Project ID: {project_id}",
|
|
508
|
+
f"Username: {username}",
|
|
509
|
+
f"Region: {region}",
|
|
510
|
+
]
|
|
511
|
+
print_boxes(messages, "OpenStack Credentials")
|
|
512
|
+
logger.info(
|
|
513
|
+
f"Using OpenStack endpoint {Fore.YELLOW}{auth_url}{Style.RESET_ALL} "
|
|
514
|
+
f"in region {Fore.YELLOW}{region}{Style.RESET_ALL}"
|
|
515
|
+
)
|
|
File without changes
|
|
File without changes
|
prowler/providers/openstack/services/compute/compute_instance_security_groups_attached/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Provider": "openstack",
|
|
3
|
+
"CheckID": "compute_instance_security_groups_attached",
|
|
4
|
+
"CheckTitle": "Compute instances have security groups attached",
|
|
5
|
+
"CheckType": [],
|
|
6
|
+
"ServiceName": "compute",
|
|
7
|
+
"SubServiceName": "",
|
|
8
|
+
"ResourceIdTemplate": "",
|
|
9
|
+
"Severity": "high",
|
|
10
|
+
"ResourceType": "OpenStackInstance",
|
|
11
|
+
"ResourceGroup": "compute",
|
|
12
|
+
"Description": "**OpenStack compute instances** (VMs) are evaluated to verify that at least one **security group** is attached. Security groups act as virtual firewalls, controlling ingress and egress traffic. Instances without security groups may have **unrestricted network access**, violating defense-in-depth principles.",
|
|
13
|
+
"Risk": "Compute instances without security groups are exposed to unrestricted network traffic, enabling:\n- **Confidentiality**: unauthorized access to services and data exfiltration\n- **Integrity**: injection attacks, tampering, and lateral movement across workloads\n- **Availability**: DDoS, port scanning, and resource exhaustion\nAttackers can probe open ports, exploit vulnerable services, and establish persistence without firewall barriers.",
|
|
14
|
+
"RelatedUrl": "",
|
|
15
|
+
"AdditionalURLs": [
|
|
16
|
+
"https://docs.openstack.org/nova/latest/user/security-groups.html",
|
|
17
|
+
"https://docs.openstack.org/api-ref/compute/",
|
|
18
|
+
"https://docs.openstack.org/neutron/latest/admin/intro-os-networking.html",
|
|
19
|
+
"https://docs.openstack.org/security-guide/networking/architecture.html"
|
|
20
|
+
],
|
|
21
|
+
"Remediation": {
|
|
22
|
+
"Code": {
|
|
23
|
+
"CLI": "openstack server add security group <instance_id> <security_group_name>",
|
|
24
|
+
"NativeIaC": "",
|
|
25
|
+
"Other": "1. Access the Horizon dashboard\n2. Navigate to **Compute > Instances**\n3. Locate instances without security groups (check the Security Groups column)\n4. Select the instance, then **Actions > Edit Security Groups**\n5. Add at least one security group (e.g., `default` or a custom group with least-privilege rules)\n6. Click **Save** and verify the attachment\n7. Test connectivity to ensure proper ingress/egress rules",
|
|
26
|
+
"Terraform": "```hcl\n# Terraform: ensure compute instance has security groups attached\nresource \"openstack_compute_instance_v2\" \"instance\" {\n name = \"example-instance\"\n image_id = var.image_id\n flavor_id = var.flavor_id\n key_pair = var.key_pair_name\n # Critical: attach at least one security group\n security_groups = [\"default\", \"web-sg\", \"app-sg\"]\n\n network {\n name = var.network_name\n }\n}\n```"
|
|
27
|
+
},
|
|
28
|
+
"Recommendation": {
|
|
29
|
+
"Text": "Apply **defense-in-depth** for network security:\n- Attach **security groups** to all instances; never deploy without firewall rules\n- Follow **least privilege**: allow only required ports and sources; deny all by default\n- Use **separate security groups** per tier (web, app, database) with explicit rules\n- Avoid overly permissive rules like `0.0.0.0/0` ingress on sensitive ports\n- Implement **network segmentation** with isolated networks or VLANs\n- Enable **flow logs** for traffic analysis and anomaly detection\n- Regularly **audit** security group rules and remove stale or overly broad permissions",
|
|
30
|
+
"Url": "https://hub.prowler.com/check/compute_instance_security_groups_attached"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"Categories": [
|
|
34
|
+
"internet-exposed",
|
|
35
|
+
"trust-boundaries"
|
|
36
|
+
],
|
|
37
|
+
"DependsOn": [],
|
|
38
|
+
"RelatedTo": [],
|
|
39
|
+
"Notes": ""
|
|
40
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from prowler.lib.check.models import Check, CheckReportOpenStack
|
|
4
|
+
from prowler.providers.openstack.services.compute.compute_client import compute_client
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class compute_instance_security_groups_attached(Check):
|
|
8
|
+
"""Ensure compute instances have security groups attached."""
|
|
9
|
+
|
|
10
|
+
def execute(self) -> List[CheckReportOpenStack]:
|
|
11
|
+
findings: List[CheckReportOpenStack] = []
|
|
12
|
+
|
|
13
|
+
for instance in compute_client.instances:
|
|
14
|
+
report = CheckReportOpenStack(metadata=self.metadata(), resource=instance)
|
|
15
|
+
report.resource_id = instance.id
|
|
16
|
+
report.resource_name = instance.name
|
|
17
|
+
report.region = instance.region
|
|
18
|
+
|
|
19
|
+
if instance.security_groups and len(instance.security_groups) > 0:
|
|
20
|
+
report.status = "PASS"
|
|
21
|
+
sg_names = ", ".join(instance.security_groups)
|
|
22
|
+
report.status_extended = (
|
|
23
|
+
f"Instance {instance.name} ({instance.id}) has security groups "
|
|
24
|
+
f"attached: {sg_names}."
|
|
25
|
+
)
|
|
26
|
+
else:
|
|
27
|
+
report.status = "FAIL"
|
|
28
|
+
report.status_extended = (
|
|
29
|
+
f"Instance {instance.name} ({instance.id}) does not have any "
|
|
30
|
+
f"security groups attached."
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
findings.append(report)
|
|
34
|
+
|
|
35
|
+
return findings
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
from openstack import exceptions as openstack_exceptions
|
|
7
|
+
|
|
8
|
+
from prowler.lib.logger import logger
|
|
9
|
+
from prowler.providers.openstack.lib.service.service import OpenStackService
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Compute(OpenStackService):
|
|
13
|
+
"""Service wrapper using openstacksdk compute APIs."""
|
|
14
|
+
|
|
15
|
+
def __init__(self, provider) -> None:
|
|
16
|
+
super().__init__(__class__.__name__, provider)
|
|
17
|
+
self.client = self.connection.compute
|
|
18
|
+
self.instances: List[ComputeInstance] = []
|
|
19
|
+
self._list_instances()
|
|
20
|
+
|
|
21
|
+
def _list_instances(self) -> None:
|
|
22
|
+
"""List all compute instances in the current project."""
|
|
23
|
+
logger.info("Compute - Listing instances...")
|
|
24
|
+
try:
|
|
25
|
+
for server in self.client.servers():
|
|
26
|
+
# Extract security group names (handle None case)
|
|
27
|
+
sg_list = getattr(server, "security_groups", None) or []
|
|
28
|
+
security_groups = [sg.get("name", "") for sg in sg_list]
|
|
29
|
+
|
|
30
|
+
self.instances.append(
|
|
31
|
+
ComputeInstance(
|
|
32
|
+
id=getattr(server, "id", ""),
|
|
33
|
+
name=getattr(server, "name", ""),
|
|
34
|
+
status=getattr(server, "status", ""),
|
|
35
|
+
flavor_id=getattr(server, "flavor", {}).get("id", ""),
|
|
36
|
+
security_groups=security_groups,
|
|
37
|
+
region=self.region,
|
|
38
|
+
project_id=self.project_id,
|
|
39
|
+
)
|
|
40
|
+
)
|
|
41
|
+
except openstack_exceptions.SDKException as error:
|
|
42
|
+
logger.error(
|
|
43
|
+
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}] -- "
|
|
44
|
+
f"Failed to list compute instances: {error}"
|
|
45
|
+
)
|
|
46
|
+
except Exception as error:
|
|
47
|
+
logger.error(
|
|
48
|
+
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}] -- "
|
|
49
|
+
f"Unexpected error listing compute instances: {error}"
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass
|
|
54
|
+
class ComputeInstance:
|
|
55
|
+
"""Represents an OpenStack compute instance (VM)."""
|
|
56
|
+
|
|
57
|
+
id: str
|
|
58
|
+
name: str
|
|
59
|
+
status: str
|
|
60
|
+
flavor_id: str
|
|
61
|
+
security_groups: List[str]
|
|
62
|
+
region: str
|
|
63
|
+
project_id: str
|