mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-09 21:14:22 +00:00
feat(azure): add cosmosdb_account_minimum_tls_version check (#11033)
Co-authored-by: Daniel Barranquero <danielbo2001@gmail.com>
This commit is contained in:
co-authored by
Daniel Barranquero
parent
406712ffa3
commit
8bc42a5ded
@@ -14,6 +14,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
|
||||
- `identity_storage_service_level_admins_scoped` check for OCI provider CIS 3.1 control 1.15, ensuring storage service-level administrators exclude delete permissions [(#11523)](https://github.com/prowler-cloud/prowler/pull/11523)
|
||||
- `cosmosdb_account_automatic_failover_enabled` check for Azure provider [(#11031)](https://github.com/prowler-cloud/prowler/pull/11031)
|
||||
- `cosmosdb_account_backup_policy_continuous` check for Azure provider [(#11032)](https://github.com/prowler-cloud/prowler/pull/11032)
|
||||
- `cosmosdb_account_minimum_tls_version` check for Azure provider, verifying Cosmos DB accounts enforce TLS 1.2 or higher for client connections [(#11033)](https://github.com/prowler-cloud/prowler/pull/11033)
|
||||
- `aks_cluster_auto_upgrade_enabled` check for Azure provider [(#11027)](https://github.com/prowler-cloud/prowler/pull/11027)
|
||||
- Jira timeout preventing the calls from hanging indefinitely when the Jira endpoint is unreachable or slow [(#11602)](https://github.com/prowler-cloud/prowler/pull/11602)
|
||||
- TLS certificate verification in the `codepipeline_project_repo_private` check, which previously used an unverified SSL context, leaving the repository-visibility probe open to MITM tampering [(#11603)](https://github.com/prowler-cloud/prowler/pull/11603)
|
||||
|
||||
@@ -1503,6 +1503,7 @@
|
||||
],
|
||||
"Checks": [
|
||||
"app_minimum_tls_version_12",
|
||||
"cosmosdb_account_minimum_tls_version",
|
||||
"monitor_storage_account_with_activity_logs_cmk_encrypted",
|
||||
"sqlserver_tde_encrypted_with_cmk",
|
||||
"sqlserver_tde_encryption_enabled",
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"Provider": "azure",
|
||||
"CheckID": "cosmosdb_account_minimum_tls_version",
|
||||
"CheckTitle": "Cosmos DB account enforces TLS 1.2 or higher",
|
||||
"CheckType": [],
|
||||
"ServiceName": "cosmosdb",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "microsoft.documentdb/databaseaccounts",
|
||||
"ResourceGroup": "database",
|
||||
"Description": "**Azure Cosmos DB accounts** are evaluated for **minimum TLS version**. TLS 1.0 and 1.1 are deprecated and contain known weaknesses. Enforcing **TLS 1.2** ensures all client connections negotiate modern, secure encryption protocols.",
|
||||
"Risk": "Allowing **TLS 1.0/1.1** exposes client connections to **POODLE**, **BEAST**, and other protocol downgrade attacks that can compromise the **confidentiality** and **integrity** of data in transit, and may enable credential interception via weakened cipher suites.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://learn.microsoft.com/en-us/azure/cosmos-db/security",
|
||||
"https://learn.microsoft.com/en-us/cli/azure/cosmosdb",
|
||||
"https://learn.microsoft.com/en-us/azure/templates/microsoft.documentdb/databaseaccounts"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "az cosmosdb update --name <COSMOS_ACCOUNT_NAME> --resource-group <RESOURCE_GROUP> --minimal-tls-version Tls12",
|
||||
"NativeIaC": "```bicep\n// Bicep: Enforce minimum TLS 1.2 on a Cosmos DB account\nresource account 'Microsoft.DocumentDB/databaseAccounts@2025-10-15' = {\n name: '<example_resource_name>'\n location: resourceGroup().location\n kind: 'GlobalDocumentDB'\n properties: {\n databaseAccountOfferType: 'Standard'\n locations: [{ locationName: resourceGroup().location }]\n minimalTlsVersion: 'Tls12' // Critical: Rejects client connections negotiating TLS 1.0 or 1.1\n }\n}\n```",
|
||||
"Other": "1. Sign in to the Azure portal and open your Cosmos DB account\n2. In the left menu, select Networking\n3. Locate the Minimum TLS version setting\n4. Select TLS 1.2\n5. Click Save\n6. Validate that all client applications support TLS 1.2 before enforcing the change",
|
||||
"Terraform": "```hcl\n# Terraform: Enforce minimum TLS 1.2 on a Cosmos DB account\nresource \"azurerm_cosmosdb_account\" \"<example_resource_name>\" {\n name = \"<example_resource_name>\"\n resource_group_name = \"<example_resource_name>\"\n location = \"<example_location>\"\n offer_type = \"Standard\"\n kind = \"GlobalDocumentDB\"\n\n geo_location {\n location = \"<example_location>\"\n failover_priority = 0\n }\n\n minimal_tls_version = \"Tls12\" # Critical: Rejects client connections negotiating TLS 1.0 or 1.1\n}\n```"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Set the Cosmos DB account **minimum TLS version** to at least **1.2** to block legacy protocols (`TLS 1.0`/`1.1`) vulnerable to known downgrade and cipher attacks. Inventory and update **client SDKs** and **drivers** to support TLS 1.2 prior to enforcement, and pair this control with **private endpoints**, **AAD/RBAC authentication**, and **handshake failure monitoring** to identify outdated clients.",
|
||||
"Url": "https://hub.prowler.com/check/cosmosdb_account_minimum_tls_version"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"encryption"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": ""
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
from prowler.lib.check.models import Check, Check_Report_Azure
|
||||
from prowler.providers.azure.services.cosmosdb.cosmosdb_client import cosmosdb_client
|
||||
|
||||
|
||||
class cosmosdb_account_minimum_tls_version(Check):
|
||||
"""Ensure that Cosmos DB accounts enforce TLS 1.2 or higher."""
|
||||
|
||||
def execute(self) -> Check_Report_Azure:
|
||||
"""Execute the Cosmos DB minimum TLS version check.
|
||||
|
||||
Iterates over every Cosmos DB account fetched by the service and reports
|
||||
PASS when `minimalTlsVersion` is `Tls12` or higher, FAIL otherwise
|
||||
(including when the property is missing or set to a legacy value).
|
||||
|
||||
Returns:
|
||||
A list of Check_Report_Azure with one report per Cosmos DB account.
|
||||
"""
|
||||
findings = []
|
||||
for subscription, accounts in cosmosdb_client.accounts.items():
|
||||
for account in accounts:
|
||||
report = Check_Report_Azure(metadata=self.metadata(), resource=account)
|
||||
report.subscription = subscription
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"CosmosDB account {account.name} from subscription {subscription} does not enforce TLS 1.2 or higher."
|
||||
if account.minimal_tls_version in {"Tls12", "Tls13"}:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"CosmosDB account {account.name} from subscription {subscription} enforces TLS 1.2 or higher."
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
from unittest import mock
|
||||
|
||||
from tests.providers.azure.azure_fixtures import (
|
||||
AZURE_SUBSCRIPTION_ID,
|
||||
set_mocked_azure_provider,
|
||||
)
|
||||
|
||||
|
||||
class Test_cosmosdb_account_minimum_tls_version:
|
||||
def test_no_subscriptions(self):
|
||||
cosmosdb_client = mock.MagicMock()
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.azure.services.cosmosdb.cosmosdb_account_minimum_tls_version.cosmosdb_account_minimum_tls_version.cosmosdb_client",
|
||||
new=cosmosdb_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.azure.services.cosmosdb.cosmosdb_account_minimum_tls_version.cosmosdb_account_minimum_tls_version import (
|
||||
cosmosdb_account_minimum_tls_version,
|
||||
)
|
||||
|
||||
cosmosdb_client.accounts = {}
|
||||
|
||||
check = cosmosdb_account_minimum_tls_version()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
def test_pass_tls12(self):
|
||||
cosmosdb_client = mock.MagicMock()
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.azure.services.cosmosdb.cosmosdb_account_minimum_tls_version.cosmosdb_account_minimum_tls_version.cosmosdb_client",
|
||||
new=cosmosdb_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.azure.services.cosmosdb.cosmosdb_account_minimum_tls_version.cosmosdb_account_minimum_tls_version import (
|
||||
cosmosdb_account_minimum_tls_version,
|
||||
)
|
||||
from prowler.providers.azure.services.cosmosdb.cosmosdb_service import (
|
||||
Account,
|
||||
)
|
||||
|
||||
cosmosdb_client.accounts = {
|
||||
AZURE_SUBSCRIPTION_ID: [
|
||||
Account(
|
||||
id="/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.DocumentDB/databaseAccounts/test-account",
|
||||
name="test-account",
|
||||
kind="GlobalDocumentDB",
|
||||
type="Microsoft.DocumentDB/databaseAccounts",
|
||||
tags={},
|
||||
is_virtual_network_filter_enabled=False,
|
||||
location="eastus",
|
||||
private_endpoint_connections=[],
|
||||
disable_local_auth=False,
|
||||
minimal_tls_version="Tls12",
|
||||
)
|
||||
]
|
||||
}
|
||||
|
||||
check = cosmosdb_account_minimum_tls_version()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert result[0].status_extended == (
|
||||
f"CosmosDB account test-account from subscription "
|
||||
f"{AZURE_SUBSCRIPTION_ID} enforces TLS 1.2 or higher."
|
||||
)
|
||||
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
|
||||
|
||||
def test_pass_tls13(self):
|
||||
cosmosdb_client = mock.MagicMock()
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.azure.services.cosmosdb.cosmosdb_account_minimum_tls_version.cosmosdb_account_minimum_tls_version.cosmosdb_client",
|
||||
new=cosmosdb_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.azure.services.cosmosdb.cosmosdb_account_minimum_tls_version.cosmosdb_account_minimum_tls_version import (
|
||||
cosmosdb_account_minimum_tls_version,
|
||||
)
|
||||
from prowler.providers.azure.services.cosmosdb.cosmosdb_service import (
|
||||
Account,
|
||||
)
|
||||
|
||||
cosmosdb_client.accounts = {
|
||||
AZURE_SUBSCRIPTION_ID: [
|
||||
Account(
|
||||
id="/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.DocumentDB/databaseAccounts/test-account",
|
||||
name="test-account",
|
||||
kind="GlobalDocumentDB",
|
||||
type="Microsoft.DocumentDB/databaseAccounts",
|
||||
tags={},
|
||||
is_virtual_network_filter_enabled=False,
|
||||
location="eastus",
|
||||
private_endpoint_connections=[],
|
||||
disable_local_auth=False,
|
||||
minimal_tls_version="Tls13",
|
||||
)
|
||||
]
|
||||
}
|
||||
|
||||
check = cosmosdb_account_minimum_tls_version()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
|
||||
def test_fail_tls11(self):
|
||||
cosmosdb_client = mock.MagicMock()
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.azure.services.cosmosdb.cosmosdb_account_minimum_tls_version.cosmosdb_account_minimum_tls_version.cosmosdb_client",
|
||||
new=cosmosdb_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.azure.services.cosmosdb.cosmosdb_account_minimum_tls_version.cosmosdb_account_minimum_tls_version import (
|
||||
cosmosdb_account_minimum_tls_version,
|
||||
)
|
||||
from prowler.providers.azure.services.cosmosdb.cosmosdb_service import (
|
||||
Account,
|
||||
)
|
||||
|
||||
cosmosdb_client.accounts = {
|
||||
AZURE_SUBSCRIPTION_ID: [
|
||||
Account(
|
||||
id="/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.DocumentDB/databaseAccounts/test-account",
|
||||
name="test-account",
|
||||
kind="GlobalDocumentDB",
|
||||
type="Microsoft.DocumentDB/databaseAccounts",
|
||||
tags={},
|
||||
is_virtual_network_filter_enabled=False,
|
||||
location="eastus",
|
||||
private_endpoint_connections=[],
|
||||
disable_local_auth=False,
|
||||
minimal_tls_version="Tls11",
|
||||
)
|
||||
]
|
||||
}
|
||||
|
||||
check = cosmosdb_account_minimum_tls_version()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert result[0].status_extended == (
|
||||
f"CosmosDB account test-account from subscription "
|
||||
f"{AZURE_SUBSCRIPTION_ID} does not enforce TLS 1.2 or higher."
|
||||
)
|
||||
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
|
||||
|
||||
def test_fail_no_tls_version(self):
|
||||
cosmosdb_client = mock.MagicMock()
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.azure.services.cosmosdb.cosmosdb_account_minimum_tls_version.cosmosdb_account_minimum_tls_version.cosmosdb_client",
|
||||
new=cosmosdb_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.azure.services.cosmosdb.cosmosdb_account_minimum_tls_version.cosmosdb_account_minimum_tls_version import (
|
||||
cosmosdb_account_minimum_tls_version,
|
||||
)
|
||||
from prowler.providers.azure.services.cosmosdb.cosmosdb_service import (
|
||||
Account,
|
||||
)
|
||||
|
||||
cosmosdb_client.accounts = {
|
||||
AZURE_SUBSCRIPTION_ID: [
|
||||
Account(
|
||||
id="/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.DocumentDB/databaseAccounts/test-account",
|
||||
name="test-account",
|
||||
kind="GlobalDocumentDB",
|
||||
type="Microsoft.DocumentDB/databaseAccounts",
|
||||
tags={},
|
||||
is_virtual_network_filter_enabled=False,
|
||||
location="eastus",
|
||||
private_endpoint_connections=[],
|
||||
disable_local_auth=False,
|
||||
minimal_tls_version=None,
|
||||
)
|
||||
]
|
||||
}
|
||||
|
||||
check = cosmosdb_account_minimum_tls_version()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
Reference in New Issue
Block a user