From eded97d73541188cefd254b7dac19d601c566b7d Mon Sep 17 00:00:00 2001 From: johannes-engler-mw <132657752+johannes-engler-mw@users.noreply.github.com> Date: Wed, 11 Dec 2024 16:37:53 +0100 Subject: [PATCH] feat(azure): check for minimal TLS version for Azure SQL server (#5745) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Rubén De la Torre Vico --- docs/tutorials/configuration_file.md | 9 + prowler/config/config.yaml | 8 + .../__init__.py | 0 ...ommended_minimal_tls_version.metadata.json | 30 +++ ...lserver_recommended_minimal_tls_version.py | 27 +++ tests/config/config_test.py | 1 + tests/config/fixtures/config.yaml | 8 + tests/providers/azure/azure_provider_test.py | 1 + ...er_recommended_minimal_tls_version_test.py | 215 ++++++++++++++++++ 9 files changed, 299 insertions(+) create mode 100644 prowler/providers/azure/services/sqlserver/sqlserver_recommended_minimal_tls_version/__init__.py create mode 100644 prowler/providers/azure/services/sqlserver/sqlserver_recommended_minimal_tls_version/sqlserver_recommended_minimal_tls_version.metadata.json create mode 100644 prowler/providers/azure/services/sqlserver/sqlserver_recommended_minimal_tls_version/sqlserver_recommended_minimal_tls_version.py create mode 100644 tests/providers/azure/services/sqlserver/sqlserver_recommended_minimal_tls_version/sqlserver_recommended_minimal_tls_version_test.py diff --git a/docs/tutorials/configuration_file.md b/docs/tutorials/configuration_file.md index 866c7f8dff..b638728b27 100644 --- a/docs/tutorials/configuration_file.md +++ b/docs/tutorials/configuration_file.md @@ -75,6 +75,7 @@ The following list includes all the Azure checks with configurable variables tha | `app_ensure_php_version_is_latest` | `php_latest_version` | String | | `app_ensure_python_version_is_latest` | `python_latest_version` | String | | `app_ensure_java_version_is_latest` | `java_latest_version` | String | +| `sqlserver_recommended_minimal_tls_version` | `recommended_minimal_tls_versions` | List of Strings | ## GCP @@ -447,6 +448,14 @@ azure: # azure.app_ensure_java_version_is_latest java_latest_version: "17" + # Azure SQL Server + # azure.sqlserver_minimal_tls_version + recommended_minimal_tls_versions: + [ + "1.2", + "1.3" + ] + # GCP Configuration gcp: # GCP Compute Configuration diff --git a/prowler/config/config.yaml b/prowler/config/config.yaml index 8b6947b458..f9641eba56 100644 --- a/prowler/config/config.yaml +++ b/prowler/config/config.yaml @@ -388,6 +388,14 @@ azure: # azure.app_ensure_java_version_is_latest java_latest_version: "17" + # Azure SQL Server + # azure.sqlserver_minimal_tls_version + recommended_minimal_tls_versions: + [ + "1.2", + "1.3", + ] + # GCP Configuration gcp: # GCP Compute Configuration diff --git a/prowler/providers/azure/services/sqlserver/sqlserver_recommended_minimal_tls_version/__init__.py b/prowler/providers/azure/services/sqlserver/sqlserver_recommended_minimal_tls_version/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/azure/services/sqlserver/sqlserver_recommended_minimal_tls_version/sqlserver_recommended_minimal_tls_version.metadata.json b/prowler/providers/azure/services/sqlserver/sqlserver_recommended_minimal_tls_version/sqlserver_recommended_minimal_tls_version.metadata.json new file mode 100644 index 0000000000..42e35c2c4f --- /dev/null +++ b/prowler/providers/azure/services/sqlserver/sqlserver_recommended_minimal_tls_version/sqlserver_recommended_minimal_tls_version.metadata.json @@ -0,0 +1,30 @@ +{ + "Provider": "azure", + "CheckID": "sqlserver_recommended_minimal_tls_version", + "CheckTitle": "Ensure SQL server has a recommended minimal TLS version required.", + "CheckType": [], + "ServiceName": "sqlserver", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "medium", + "ResourceType": "SQLServer", + "Description": "Ensure that SQL Server instances are configured with the recommended minimal TLS version to maintain secure connections.", + "Risk": "Using outdated or weak TLS versions can expose SQL Server instances to vulnerabilities, increasing the risk of data breaches and unauthorized access.", + "RelatedUrl": "https://learn.microsoft.com/en-us/azure/azure-sql/database/connectivity-settings?view=azuresql&tabs=azure-portal#configure-minimum-tls-version", + "Remediation": { + "Code": { + "CLI": "az sql server update -n sql-server-name -g sql-server-group --set minimalTlsVersion=", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "1. Go to Azure SQL Server 2. Navigate to 'Security' -> 'Networking' 3. Select 'Connectivity' 4. Update the TLS version in the field 'Minimum TLS version' to a recommended minimal version (e.g., TLS 1.2).", + "Url": "https://learn.microsoft.com/en-us/azure/azure-sql/database/connectivity-settings?view=azuresql&tabs=azure-portal#configure-minimum-tls-version" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "Verify support for the TLS version from the application side before changing the minimal version." +} diff --git a/prowler/providers/azure/services/sqlserver/sqlserver_recommended_minimal_tls_version/sqlserver_recommended_minimal_tls_version.py b/prowler/providers/azure/services/sqlserver/sqlserver_recommended_minimal_tls_version/sqlserver_recommended_minimal_tls_version.py new file mode 100644 index 0000000000..d0f97b8861 --- /dev/null +++ b/prowler/providers/azure/services/sqlserver/sqlserver_recommended_minimal_tls_version/sqlserver_recommended_minimal_tls_version.py @@ -0,0 +1,27 @@ +from typing import List + +from prowler.lib.check.models import Check, Check_Report_Azure +from prowler.providers.azure.services.sqlserver.sqlserver_client import sqlserver_client + + +class sqlserver_recommended_minimal_tls_version(Check): + def execute(self) -> List[Check_Report_Azure]: + findings = [] + recommended_minimal_tls_versions = sqlserver_client.audit_config.get( + "recommended_minimal_tls_versions", ["1.2", "1.3"] + ) + for subscription, sql_servers in sqlserver_client.sql_servers.items(): + for sql_server in sql_servers: + report = Check_Report_Azure(self.metadata()) + report.subscription = subscription + report.resource_name = sql_server.name + report.resource_id = sql_server.id + report.status = "FAIL" + report.location = sql_server.location + report.status_extended = f"SQL Server {sql_server.name} from subscription {subscription} is using TLS version {sql_server.minimal_tls_version} as minimal accepted which is not recommended. Please use one of the recommended versions: {', '.join(recommended_minimal_tls_versions)}." + if sql_server.minimal_tls_version in recommended_minimal_tls_versions: + report.status_extended = f"SQL Server {sql_server.name} from subscription {subscription} is using version {sql_server.minimal_tls_version} as minimal accepted which is recommended." + report.status = "PASS" + findings.append(report) + + return findings diff --git a/tests/config/config_test.py b/tests/config/config_test.py index f79ef50eff..14e0077a88 100644 --- a/tests/config/config_test.py +++ b/tests/config/config_test.py @@ -318,6 +318,7 @@ config_azure = { "php_latest_version": "8.2", "python_latest_version": "3.12", "java_latest_version": "17", + "recommended_minimal_tls_versions": ["1.2", "1.3"], } config_gcp = {"shodan_api_key": None} diff --git a/tests/config/fixtures/config.yaml b/tests/config/fixtures/config.yaml index 54b1c011b3..0994a496dc 100644 --- a/tests/config/fixtures/config.yaml +++ b/tests/config/fixtures/config.yaml @@ -379,6 +379,14 @@ azure: # azure.app_ensure_java_version_is_latest java_latest_version: "17" + # Azure SQL Server + # azure.sqlserver_minimal_tls_version + recommended_minimal_tls_versions: + [ + "1.2", + "1.3" + ] + # GCP Configuration gcp: # GCP Compute Configuration diff --git a/tests/providers/azure/azure_provider_test.py b/tests/providers/azure/azure_provider_test.py index ea798ad057..853903fcc2 100644 --- a/tests/providers/azure/azure_provider_test.py +++ b/tests/providers/azure/azure_provider_test.py @@ -81,6 +81,7 @@ class TestAzureProvider: "php_latest_version": "8.2", "python_latest_version": "3.12", "java_latest_version": "17", + "recommended_minimal_tls_versions": ["1.2", "1.3"], } def test_azure_provider_not_auth_methods(self): diff --git a/tests/providers/azure/services/sqlserver/sqlserver_recommended_minimal_tls_version/sqlserver_recommended_minimal_tls_version_test.py b/tests/providers/azure/services/sqlserver/sqlserver_recommended_minimal_tls_version/sqlserver_recommended_minimal_tls_version_test.py new file mode 100644 index 0000000000..2b80067468 --- /dev/null +++ b/tests/providers/azure/services/sqlserver/sqlserver_recommended_minimal_tls_version/sqlserver_recommended_minimal_tls_version_test.py @@ -0,0 +1,215 @@ +from unittest import mock +from uuid import uuid4 + +from azure.mgmt.sql.models import TransparentDataEncryption + +from prowler.providers.azure.services.sqlserver.sqlserver_service import ( + Database, + Server, +) +from tests.providers.azure.azure_fixtures import ( + AZURE_SUBSCRIPTION_ID, + set_mocked_azure_provider, +) + + +class Test_sqlserver_recommended_minimal_tls_version: + def test_no_sql_servers(self): + sqlserver_client = mock.MagicMock + sqlserver_client.sql_servers = {} + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_azure_provider(), + ), mock.patch( + "prowler.providers.azure.services.sqlserver.sqlserver_recommended_minimal_tls_version.sqlserver_recommended_minimal_tls_version.sqlserver_client", + new=sqlserver_client, + ): + from prowler.providers.azure.services.sqlserver.sqlserver_recommended_minimal_tls_version.sqlserver_recommended_minimal_tls_version import ( + sqlserver_recommended_minimal_tls_version, + ) + + sqlserver_client.audit_config = { + "recommended_minimal_tls_versions": ["1.2", "1.3"] + } + + check = sqlserver_recommended_minimal_tls_version() + result = check.execute() + assert len(result) == 0 + + def test_sql_servers_deprecated_minimal_tls_version(self): + sqlserver_client = mock.MagicMock + sql_server_name = "SQL Server Name" + sql_server_id = str(uuid4()) + database_name = "Database Name" + database_id = str(uuid4()) + database = Database( + id=database_id, + name=database_name, + type="type", + location="location", + managed_by="managed_by", + tde_encryption=TransparentDataEncryption(status="Disabled"), + ) + sqlserver_client.sql_servers = { + AZURE_SUBSCRIPTION_ID: [ + Server( + id=sql_server_id, + name=sql_server_name, + public_network_access="", + minimal_tls_version="1.0", + administrators=None, + auditing_policies=None, + firewall_rules=None, + databases=[database], + encryption_protector=None, + location="location", + ) + ] + } + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_azure_provider(), + ), mock.patch( + "prowler.providers.azure.services.sqlserver.sqlserver_recommended_minimal_tls_version.sqlserver_recommended_minimal_tls_version.sqlserver_client", + new=sqlserver_client, + ): + from prowler.providers.azure.services.sqlserver.sqlserver_recommended_minimal_tls_version.sqlserver_recommended_minimal_tls_version import ( + sqlserver_recommended_minimal_tls_version, + ) + + sqlserver_client.audit_config = { + "recommended_minimal_tls_versions": ["1.2", "1.3"] + } + + check = sqlserver_recommended_minimal_tls_version() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"SQL Server {sql_server_name} from subscription {AZURE_SUBSCRIPTION_ID} is using TLS version 1.0 as minimal accepted which is not recommended. Please use one of the recommended versions: {', '.join(sqlserver_client.audit_config['recommended_minimal_tls_versions'])}." + ) + assert result[0].subscription == AZURE_SUBSCRIPTION_ID + assert result[0].resource_name == sql_server_name + assert result[0].resource_id == sql_server_id + assert result[0].location == "location" + + def test_sql_servers_no_minimal_tls_version(self): + sqlserver_client = mock.MagicMock + sql_server_name = "SQL Server Name" + sql_server_id = str(uuid4()) + database_name = "Database Name" + database_id = str(uuid4()) + database = Database( + id=database_id, + name=database_name, + type="type", + location="location", + managed_by="managed_by", + tde_encryption=TransparentDataEncryption(status="Disabled"), + ) + sqlserver_client.sql_servers = { + AZURE_SUBSCRIPTION_ID: [ + Server( + id=sql_server_id, + name=sql_server_name, + public_network_access="", + minimal_tls_version="", + administrators=None, + auditing_policies=None, + firewall_rules=None, + databases=[database], + encryption_protector=None, + location="location", + ) + ] + } + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_azure_provider(), + ), mock.patch( + "prowler.providers.azure.services.sqlserver.sqlserver_recommended_minimal_tls_version.sqlserver_recommended_minimal_tls_version.sqlserver_client", + new=sqlserver_client, + ): + from prowler.providers.azure.services.sqlserver.sqlserver_recommended_minimal_tls_version.sqlserver_recommended_minimal_tls_version import ( + sqlserver_recommended_minimal_tls_version, + ) + + sqlserver_client.audit_config = { + "recommended_minimal_tls_versions": ["1.2", "1.3"] + } + + check = sqlserver_recommended_minimal_tls_version() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"SQL Server {sql_server_name} from subscription {AZURE_SUBSCRIPTION_ID} is using TLS version as minimal accepted which is not recommended. Please use one of the recommended versions: {', '.join(sqlserver_client.audit_config['recommended_minimal_tls_versions'])}." + ) + assert result[0].subscription == AZURE_SUBSCRIPTION_ID + assert result[0].resource_name == sql_server_name + assert result[0].resource_id == sql_server_id + assert result[0].location == "location" + + def test_sql_servers_minimal_tls_version(self): + sqlserver_client = mock.MagicMock + sql_server_name = "SQL Server Name" + sql_server_id = str(uuid4()) + database_name = "Database Name" + database_id = str(uuid4()) + database = Database( + id=database_id, + name=database_name, + type="type", + location="location", + managed_by="managed_by", + tde_encryption=TransparentDataEncryption(status="Enabled"), + ) + sqlserver_client.sql_servers = { + AZURE_SUBSCRIPTION_ID: [ + Server( + id=sql_server_id, + name=sql_server_name, + public_network_access="", + minimal_tls_version="1.2", + administrators=None, + auditing_policies=None, + firewall_rules=None, + databases=[database], + encryption_protector=None, + location="location", + ) + ] + } + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_azure_provider(), + ), mock.patch( + "prowler.providers.azure.services.sqlserver.sqlserver_recommended_minimal_tls_version.sqlserver_recommended_minimal_tls_version.sqlserver_client", + new=sqlserver_client, + ): + from prowler.providers.azure.services.sqlserver.sqlserver_recommended_minimal_tls_version.sqlserver_recommended_minimal_tls_version import ( + sqlserver_recommended_minimal_tls_version, + ) + + sqlserver_client.audit_config = { + "recommended_minimal_tls_versions": ["1.2", "1.3"] + } + + check = sqlserver_recommended_minimal_tls_version() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"SQL Server {sql_server_name} from subscription {AZURE_SUBSCRIPTION_ID} is using version 1.2 as minimal accepted which is recommended." + ) + assert result[0].subscription == AZURE_SUBSCRIPTION_ID + assert result[0].resource_name == sql_server_name + assert result[0].resource_id == sql_server_id + assert result[0].location == "location"