feat(azure): add new check databricks_workspace_cmk_encryption_enabled (#8017)

This commit is contained in:
Rubén De la Torre Vico
2025-06-18 12:36:37 +02:00
committed by GitHub
parent facc0627d7
commit f9aed36d0b
7 changed files with 275 additions and 1 deletions
+1
View File
@@ -35,6 +35,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
- `codebuild_project_uses_allowed_github_organizations` check for AWS provider [(#7595)](https://github.com/prowler-cloud/prowler/pull/7595)
- IaC provider [(#7852)](https://github.com/prowler-cloud/prowler/pull/7852)
- Azure Databricks service integration for Azure provider, including the `databricks_workspace_vnet_injection_enabled` check [(#8008)](https://github.com/prowler-cloud/prowler/pull/8008)
- Azure Databricks check `databricks_workspace_cmk_encryption_enabled` to ensure workspaces use customer-managed keys (CMK) for encryption at rest [(#8017)](https://github.com/prowler-cloud/prowler/pull/8017)
---
@@ -41,6 +41,26 @@ class Databricks(AzureService):
for workspace in client.workspaces.list_by_subscription():
workspace_parameters = getattr(workspace, "parameters", None)
workspace_managed_disk_encryption = getattr(
getattr(
getattr(workspace, "encryption", None), "entities", None
),
"managed_disk",
None,
)
key_vault_properties = getattr(
workspace_managed_disk_encryption, "key_vault_properties", None
)
if key_vault_properties:
managed_disk_encryption = ManagedDiskEncryption(
key_name=key_vault_properties.key_name,
key_version=key_vault_properties.key_version,
key_vault_uri=key_vault_properties.key_vault_uri,
)
else:
managed_disk_encryption = None
workspaces[subscription][workspace.id] = DatabricksWorkspace(
id=workspace.id,
@@ -55,6 +75,7 @@ class Databricks(AzureService):
)
else None
),
managed_disk_encryption=managed_disk_encryption,
)
except Exception as error:
logger.error(
@@ -63,6 +84,21 @@ class Databricks(AzureService):
return workspaces
class ManagedDiskEncryption(BaseModel):
"""
Pydantic model representing the encryption settings for a workspace's managed disks.
Attributes:
key_name: The name of the key used for encryption.
key_version: The version of the key used for encryption.
key_vault_uri: The URI of the key vault containing the key used for encryption.
"""
key_name: str
key_version: str
key_vault_uri: str
class DatabricksWorkspace(BaseModel):
"""
Pydantic model representing an Azure Databricks workspace.
@@ -72,9 +108,11 @@ class DatabricksWorkspace(BaseModel):
name: The name of the workspace.
location: The Azure region where the workspace is deployed.
custom_managed_vnet_id: The ID of the custom managed virtual network, if configured.
managed_disk_encryption: The encryption settings for the workspace's managed disks.
"""
id: str
name: str
location: str
custom_managed_vnet_id: Optional[str]
custom_managed_vnet_id: Optional[str] = None
managed_disk_encryption: Optional[ManagedDiskEncryption] = None
@@ -0,0 +1,30 @@
{
"Provider": "azure",
"CheckID": "databricks_workspace_cmk_encryption_enabled",
"CheckTitle": "Ensure Azure Databricks workspaces use customer-managed keys (CMK) for encryption at rest",
"CheckType": [],
"ServiceName": "databricks",
"SubServiceName": "workspace",
"ResourceIdTemplate": "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Databricks/workspaces/{workspaceName}",
"Severity": "high",
"ResourceType": "AzureDatabricksWorkspace",
"Description": "Checks whether Azure Databricks workspaces are configured to use customer-managed keys (CMK) for encryption at rest, providing greater control over data encryption and compliance.",
"Risk": "Without CMK, organizations have less control over encryption keys, which may impact regulatory compliance and increase risk of unauthorized data access.",
"RelatedUrl": "https://learn.microsoft.com/en-us/azure/databricks/security/keys/customer-managed-keys",
"Remediation": {
"Code": {
"CLI": "az databricks workspace update --name <databricks-workspace-name> --resource-group <resource-group-name> --prepare-encryption && databricks workspace update --name <databricks-workspace-name> --resource-group <resource-group-name> --key-source 'Microsoft.KeyVault' --key-name <key-name> --key-vault <key-vault-uri> --key-version <key-version>",
"NativeIaC": "",
"Other": "",
"Terraform": ""
},
"Recommendation": {
"Text": "Enable customer-managed keys (CMK) for Databricks workspaces using Azure Key Vault to enhance control over data encryption, auditing, and compliance.",
"Url": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/azure/Databricks/enable-encryption-with-cmk.html"
}
},
"Categories": [],
"DependsOn": [],
"RelatedTo": [],
"Notes": "Customer-managed key (CMK) encryption is only available for Databricks workspaces on the Premium tier."
}
@@ -0,0 +1,33 @@
from prowler.lib.check.models import Check, Check_Report_Azure
from prowler.providers.azure.services.databricks.databricks_client import (
databricks_client,
)
class databricks_workspace_cmk_encryption_enabled(Check):
"""
Ensure Azure Databricks workspaces use customer-managed keys (CMK) for encryption at rest.
This check evaluates whether each Azure Databricks workspace in the subscription is configured to use a customer-managed key (CMK) for encrypting data at rest.
- PASS: The workspace has CMK encryption enabled (managed_disk_encryption is set).
- FAIL: The workspace does not have CMK encryption enabled.
"""
def execute(self):
findings = []
for subscription, workspaces in databricks_client.workspaces.items():
for workspace in workspaces.values():
report = Check_Report_Azure(
metadata=self.metadata(), resource=workspace
)
report.subscription = subscription
enc = workspace.managed_disk_encryption
if enc:
report.status = "PASS"
report.status_extended = f"Databricks workspace {workspace.name} in subscription {subscription} has customer-managed key (CMK) encryption enabled with key {enc.key_vault_uri}/{enc.key_name}/{enc.key_version}."
else:
report.status = "FAIL"
report.status_extended = f"Databricks workspace {workspace.name} in subscription {subscription} does not have customer-managed key (CMK) encryption enabled."
findings.append(report)
return findings
@@ -3,6 +3,7 @@ from unittest.mock import patch
from prowler.providers.azure.services.databricks.databricks_service import (
Databricks,
DatabricksWorkspace,
ManagedDiskEncryption,
)
from tests.providers.azure.azure_fixtures import (
AZURE_SUBSCRIPTION_ID,
@@ -18,6 +19,11 @@ def mock_databricks_get_workspaces(_):
name="test-workspace",
location="eastus",
custom_managed_vnet_id="test-vnet-id",
managed_disk_encryption=ManagedDiskEncryption(
key_name="test-key",
key_version="test-version",
key_vault_uri="test-vault-uri",
),
)
}
}
@@ -48,3 +54,39 @@ class Test_Databricks_Service:
assert workspace.name == "test-workspace"
assert workspace.location == "eastus"
assert workspace.custom_managed_vnet_id == "test-vnet-id"
assert (
workspace.managed_disk_encryption.__class__.__name__
== "ManagedDiskEncryption"
)
assert workspace.managed_disk_encryption.key_name == "test-key"
assert workspace.managed_disk_encryption.key_version == "test-version"
assert workspace.managed_disk_encryption.key_vault_uri == "test-vault-uri"
def mock_databricks_get_workspaces_no_encryption(_):
return {
AZURE_SUBSCRIPTION_ID: {
"test-workspace-id": DatabricksWorkspace(
id="test-workspace-id",
name="test-workspace",
location="eastus",
custom_managed_vnet_id="test-vnet-id",
managed_disk_encryption=None,
)
}
}
@patch(
"prowler.providers.azure.services.databricks.databricks_service.Databricks._get_workspaces",
new=mock_databricks_get_workspaces_no_encryption,
)
class Test_Databricks_Service_No_Encryption:
def test_get_workspaces_no_encryption(self):
databricks = Databricks(set_mocked_azure_provider())
workspace = databricks.workspaces[AZURE_SUBSCRIPTION_ID]["test-workspace-id"]
assert workspace.id == "test-workspace-id"
assert workspace.name == "test-workspace"
assert workspace.location == "eastus"
assert workspace.custom_managed_vnet_id == "test-vnet-id"
assert workspace.managed_disk_encryption is None
@@ -0,0 +1,130 @@
from unittest import mock
from uuid import uuid4
from prowler.providers.azure.services.databricks.databricks_service import (
DatabricksWorkspace,
ManagedDiskEncryption,
)
from tests.providers.azure.azure_fixtures import (
AZURE_SUBSCRIPTION_ID,
set_mocked_azure_provider,
)
class Test_databricks_workspace_cmk_encryption_enabled:
def test_no_databricks_workspaces(self):
databricks_client = mock.MagicMock
databricks_client.workspaces = {}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.databricks.databricks_workspace_cmk_encryption_enabled.databricks_workspace_cmk_encryption_enabled.databricks_client",
new=databricks_client,
),
):
from prowler.providers.azure.services.databricks.databricks_workspace_cmk_encryption_enabled.databricks_workspace_cmk_encryption_enabled import (
databricks_workspace_cmk_encryption_enabled,
)
check = databricks_workspace_cmk_encryption_enabled()
result = check.execute()
assert len(result) == 0
def test_databricks_workspace_cmk_encryption_disabled(self):
workspace_id = str(uuid4())
workspace_name = "test-workspace"
databricks_client = mock.MagicMock
databricks_client.workspaces = {
AZURE_SUBSCRIPTION_ID: {
workspace_id: DatabricksWorkspace(
id=workspace_id,
name=workspace_name,
location="eastus",
custom_managed_vnet_id=None,
managed_disk_encryption=None,
)
}
}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.databricks.databricks_workspace_cmk_encryption_enabled.databricks_workspace_cmk_encryption_enabled.databricks_client",
new=databricks_client,
),
):
from prowler.providers.azure.services.databricks.databricks_workspace_cmk_encryption_enabled.databricks_workspace_cmk_encryption_enabled import (
databricks_workspace_cmk_encryption_enabled,
)
check = databricks_workspace_cmk_encryption_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"Databricks workspace {workspace_name} in subscription {AZURE_SUBSCRIPTION_ID} does not have customer-managed key (CMK) encryption enabled."
)
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
assert result[0].resource_name == workspace_name
assert result[0].resource_id == workspace_id
assert result[0].location == "eastus"
def test_databricks_workspace_cmk_encryption_enabled(self):
workspace_id = str(uuid4())
workspace_name = "test-workspace"
key_name = "test-key"
key_version = "test-version"
key_vault_uri = "test-vault-uri"
databricks_client = mock.MagicMock
databricks_client.workspaces = {
AZURE_SUBSCRIPTION_ID: {
workspace_id: DatabricksWorkspace(
id=workspace_id,
name=workspace_name,
location="eastus",
custom_managed_vnet_id=None,
managed_disk_encryption=ManagedDiskEncryption(
key_name=key_name,
key_version=key_version,
key_vault_uri=key_vault_uri,
),
)
}
}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.databricks.databricks_workspace_cmk_encryption_enabled.databricks_workspace_cmk_encryption_enabled.databricks_client",
new=databricks_client,
),
):
from prowler.providers.azure.services.databricks.databricks_workspace_cmk_encryption_enabled.databricks_workspace_cmk_encryption_enabled import (
databricks_workspace_cmk_encryption_enabled,
)
check = databricks_workspace_cmk_encryption_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"Databricks workspace {workspace_name} in subscription {AZURE_SUBSCRIPTION_ID} has customer-managed key (CMK) encryption enabled with key {key_vault_uri}/{key_name}/{key_version}."
)
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
assert result[0].resource_name == workspace_name
assert result[0].resource_id == workspace_id
assert result[0].location == "eastus"