feat(azure): add new check keyvault_access_only_through_private_endpoints (#8072)

Co-authored-by: Sergio Garcia <hello@mistercloudsec.com>
This commit is contained in:
Rubén De la Torre Vico
2025-06-24 16:04:02 +02:00
committed by GitHub
parent 5b1e7bb7f9
commit cbf2a28bac
6 changed files with 270 additions and 0 deletions
+1
View File
@@ -37,6 +37,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
- 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)
- Add `storage_account_default_to_entra_authorization_enabled` check for Azure provider. [(#7981)](https://github.com/prowler-cloud/prowler/pull/7981)
- `keyvault_ensure_public_network_access_disabled` check for Azure provider. [(#8072)](https://github.com/prowler-cloud/prowler/pull/8072)
- New check `monitor_alert_service_health_exists` for Azure provider [(#8067)](https://github.com/prowler-cloud/prowler/pull/8067)
- Replace `Domain.Read.All` with `Directory.Read.All` in Azure and M365 docs [(#8075)](https://github.com/prowler-cloud/prowler/pull/8075)
@@ -0,0 +1,30 @@
{
"Provider": "azure",
"CheckID": "keyvault_access_only_through_private_endpoints",
"CheckTitle": "Ensure that public network access when using private endpoint is disabled.",
"CheckType": [],
"ServiceName": "keyvault",
"SubServiceName": "",
"ResourceIdTemplate": "/subscriptions/{subscription_id}/resourceGroups/{resource_group}/providers/Microsoft.KeyVault/vaults/{vault_name}",
"Severity": "high",
"ResourceType": "KeyVault",
"Description": "Checks if Key Vaults with private endpoints have public network access disabled.",
"Risk": "Allowing public network access to Key Vault when using private endpoint can expose sensitive data to unauthorized access.",
"RelatedUrl": "https://learn.microsoft.com/en-us/azure/key-vault/general/network-security",
"Remediation": {
"Code": {
"CLI": "az keyvault update --resource-group <resource_group> --name <vault_name> --public-network-access disabled",
"NativeIaC": "{\n \"type\": \"Microsoft.KeyVault/vaults\",\n \"apiVersion\": \"2022-07-01\",\n \"properties\": {\n \"publicNetworkAccess\": \"disabled\"\n }\n}",
"Terraform": "resource \"azurerm_key_vault\" \"example\" {\n # ... other configuration ...\n\n public_network_access_enabled = false\n}",
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/azure/KeyVault/use-private-endpoints.html"
},
"Recommendation": {
"Text": "Disable public network access for Key Vaults that use private endpoint to ensure network traffic only flows through the private endpoint.",
"Url": "https://learn.microsoft.com/en-us/azure/private-link/private-endpoint-overview"
}
},
"Categories": [],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
@@ -0,0 +1,37 @@
from prowler.lib.check.models import Check, Check_Report_Azure
from prowler.providers.azure.services.keyvault.keyvault_client import keyvault_client
class keyvault_access_only_through_private_endpoints(Check):
"""
Ensure that Public Network Access when using Private Endpoint is disabled.
This check evaluates whether Azure Key Vaults with private endpoints configured have
public network access disabled. Disabling public network access enhances security by
isolating the Key Vault from the public internet, thereby reducing its exposure.
- PASS: The Key Vault has private endpoints and public network access is disabled.
- FAIL: The Key Vault has private endpoints and public network access is enabled.
"""
def execute(self) -> Check_Report_Azure:
findings = []
for subscription, key_vaults in keyvault_client.key_vaults.items():
for keyvault in key_vaults:
if (
keyvault.properties
and keyvault.properties.private_endpoint_connections
):
report = Check_Report_Azure(
metadata=self.metadata(), resource=keyvault
)
report.subscription = subscription
if keyvault.properties.public_network_access_disabled:
report.status = "PASS"
report.status_extended = f"Keyvault {keyvault.name} from subscription {subscription} has public network access disabled and is using private endpoints."
else:
report.status = "FAIL"
report.status_extended = f"Keyvault {keyvault.name} from subscription {subscription} has public network access enabled while using private endpoints."
findings.append(report)
return findings
@@ -70,6 +70,14 @@ class KeyVault(AzureService):
"enable_purge_protection",
False,
),
public_network_access_disabled=(
getattr(
keyvault_properties,
"public_network_access",
"Enabled",
)
== "Disabled"
),
),
keys=keys,
secrets=secrets,
@@ -247,6 +255,7 @@ class VaultProperties:
private_endpoint_connections: List[PrivateEndpointConnection]
enable_soft_delete: bool
enable_purge_protection: bool
public_network_access_disabled: bool = False
@dataclass
@@ -0,0 +1,193 @@
from unittest import mock
from uuid import uuid4
from tests.providers.azure.azure_fixtures import (
AZURE_SUBSCRIPTION_ID,
set_mocked_azure_provider,
)
class Test_keyvault_access_only_through_private_endpoints:
def test_no_key_vaults(self):
keyvault_client = mock.MagicMock
keyvault_client.key_vaults = {}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.keyvault.keyvault_access_only_through_private_endpoints.keyvault_access_only_through_private_endpoints.keyvault_client",
new=keyvault_client,
),
):
from prowler.providers.azure.services.keyvault.keyvault_access_only_through_private_endpoints.keyvault_access_only_through_private_endpoints import (
keyvault_access_only_through_private_endpoints,
)
check = keyvault_access_only_through_private_endpoints()
result = check.execute()
assert len(result) == 0
def test_key_vaults_no_private_endpoints(self):
keyvault_client = mock.MagicMock
keyvault_name = "Keyvault Name"
keyvault_id = str(uuid4())
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.keyvault.keyvault_access_only_through_private_endpoints.keyvault_access_only_through_private_endpoints.keyvault_client",
new=keyvault_client,
),
):
from prowler.providers.azure.services.keyvault.keyvault_access_only_through_private_endpoints.keyvault_access_only_through_private_endpoints import (
keyvault_access_only_through_private_endpoints,
)
from prowler.providers.azure.services.keyvault.keyvault_service import (
KeyVaultInfo,
VaultProperties,
)
keyvault_client.key_vaults = {
AZURE_SUBSCRIPTION_ID: [
KeyVaultInfo(
id=keyvault_id,
name=keyvault_name,
location="westeurope",
resource_group="resource_group",
properties=VaultProperties(
tenant_id="tenantid",
enable_rbac_authorization=False,
private_endpoint_connections=[],
enable_soft_delete=True,
enable_purge_protection=True,
public_network_access_disabled=False,
),
)
]
}
check = keyvault_access_only_through_private_endpoints()
result = check.execute()
assert len(result) == 0
def test_key_vaults_with_private_endpoints_public_access_enabled(self):
keyvault_client = mock.MagicMock
keyvault_name = "Keyvault Name"
keyvault_id = str(uuid4())
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.keyvault.keyvault_access_only_through_private_endpoints.keyvault_access_only_through_private_endpoints.keyvault_client",
new=keyvault_client,
),
):
from prowler.providers.azure.services.keyvault.keyvault_access_only_through_private_endpoints.keyvault_access_only_through_private_endpoints import (
keyvault_access_only_through_private_endpoints,
)
from prowler.providers.azure.services.keyvault.keyvault_service import (
KeyVaultInfo,
PrivateEndpointConnection,
VaultProperties,
)
keyvault_client.key_vaults = {
AZURE_SUBSCRIPTION_ID: [
KeyVaultInfo(
id=keyvault_id,
name=keyvault_name,
location="westeurope",
resource_group="resource_group",
properties=VaultProperties(
tenant_id="tenantid",
enable_rbac_authorization=True,
private_endpoint_connections=[
PrivateEndpointConnection(id="id1")
],
enable_soft_delete=True,
enable_purge_protection=True,
public_network_access_disabled=False,
),
)
]
}
check = keyvault_access_only_through_private_endpoints()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"Keyvault {keyvault_name} from subscription {AZURE_SUBSCRIPTION_ID} has public network access enabled while using private endpoints."
)
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
assert result[0].resource_name == keyvault_name
assert result[0].resource_id == keyvault_id
assert result[0].location == "westeurope"
def test_key_vaults_with_private_endpoints_public_access_disabled(self):
keyvault_client = mock.MagicMock
keyvault_name = "Keyvault Name"
keyvault_id = str(uuid4())
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.keyvault.keyvault_access_only_through_private_endpoints.keyvault_access_only_through_private_endpoints.keyvault_client",
new=keyvault_client,
),
):
from prowler.providers.azure.services.keyvault.keyvault_access_only_through_private_endpoints.keyvault_access_only_through_private_endpoints import (
keyvault_access_only_through_private_endpoints,
)
from prowler.providers.azure.services.keyvault.keyvault_service import (
KeyVaultInfo,
PrivateEndpointConnection,
VaultProperties,
)
keyvault_client.key_vaults = {
AZURE_SUBSCRIPTION_ID: [
KeyVaultInfo(
id=keyvault_id,
name=keyvault_name,
location="westeurope",
resource_group="resource_group",
properties=VaultProperties(
tenant_id="tenantid",
enable_rbac_authorization=True,
private_endpoint_connections=[
PrivateEndpointConnection(id="id1")
],
enable_soft_delete=True,
enable_purge_protection=True,
public_network_access_disabled=True,
),
)
]
}
check = keyvault_access_only_through_private_endpoints()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"Keyvault {keyvault_name} from subscription {AZURE_SUBSCRIPTION_ID} has public network access disabled and is using private endpoints."
)
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
assert result[0].resource_name == keyvault_name
assert result[0].resource_id == keyvault_id
assert result[0].location == "westeurope"