mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-04 19:21:51 +00:00
feat(azure): add aks_cluster_defender_enabled check (#11028)
Co-authored-by: Hugo P.Brito <hugopbrit@gmail.com>
This commit is contained in:
@@ -28,6 +28,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
|
||||
- Rename DORA to DORA_2022_2554 to follow the naming <name>_<version> in compliance frameworks [(#11551)](https://github.com/prowler-cloud/prowler/pull/11551)
|
||||
- `entra_directory_sync_object_takeover_blocked` check for the M365 provider, verifying that hybrid Entra tenants block cloud object takeover through both soft-match and hard-match directory synchronization [(#11098)](https://github.com/prowler-cloud/prowler/pull/11098)
|
||||
- `entra_conditional_access_policy_no_deleted_object_references` check for M365 provider [(#11236)](https://github.com/prowler-cloud/prowler/pull/11236)
|
||||
- `aks_cluster_defender_enabled` check for Azure provider, verifying that AKS clusters have Microsoft Defender security monitoring enabled [(#11028)](https://github.com/prowler-cloud/prowler/pull/11028)
|
||||
|
||||
### 🔄 Changed
|
||||
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"Provider": "azure",
|
||||
"CheckID": "aks_cluster_defender_enabled",
|
||||
"CheckTitle": "AKS cluster has Microsoft Defender security profile enabled",
|
||||
"CheckType": [],
|
||||
"ServiceName": "aks",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "high",
|
||||
"ResourceType": "microsoft.containerservice/managedclusters",
|
||||
"ResourceGroup": "container",
|
||||
"Description": "**AKS clusters** are evaluated for the **Microsoft Defender security profile** (`securityProfile.defender.securityMonitoring.enabled=true`). Defender for Containers extends security monitoring, threat detection, vulnerability assessment, and security posture insights to Azure Kubernetes Service workloads.",
|
||||
"Risk": "Without the Microsoft Defender security profile, AKS runtime threats such as **cryptomining**, suspicious Kubernetes API activity, vulnerable container images, and malicious workload behavior may go undetected. Compromised containers can pivot to cluster resources and cloud APIs, impacting **confidentiality**, **integrity**, and **availability**.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://learn.microsoft.com/en-us/azure/defender-for-cloud/defender-for-containers-deployment-overview",
|
||||
"https://learn.microsoft.com/en-us/cli/azure/aks?view=azure-cli-latest#az-aks-update",
|
||||
"https://learn.microsoft.com/en-us/azure/templates/microsoft.containerservice/managedclusters"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "az aks update --resource-group <RESOURCE_GROUP> --name <CLUSTER_NAME> --enable-defender",
|
||||
"NativeIaC": "```bicep\n// AKS cluster with Microsoft Defender security monitoring enabled\nresource aks 'Microsoft.ContainerService/managedClusters@2024-05-01' = {\n name: '<CLUSTER_NAME>'\n location: '<LOCATION>'\n identity: {\n type: 'SystemAssigned'\n }\n properties: {\n dnsPrefix: '<DNS_PREFIX>'\n securityProfile: {\n defender: {\n logAnalyticsWorkspaceResourceId: '<LOG_ANALYTICS_WORKSPACE_RESOURCE_ID>'\n securityMonitoring: {\n enabled: true // Critical: enables Defender security monitoring for AKS\n }\n }\n }\n agentPoolProfiles: [\n {\n name: 'system'\n count: 1\n vmSize: 'Standard_DS2_v2'\n mode: 'System'\n }\n ]\n }\n}\n```",
|
||||
"Other": "1. In Microsoft Defender for Cloud, enable the Containers plan for the subscription that contains the AKS cluster\n2. In the AKS cluster configuration, enable the Microsoft Defender security profile\n3. Verify that Defender security monitoring is enabled for the managed cluster",
|
||||
"Terraform": "```hcl\nresource \"azurerm_log_analytics_workspace\" \"example\" {\n name = \"<workspace-name>\"\n location = \"<location>\"\n resource_group_name = \"<resource-group>\"\n sku = \"PerGB2018\"\n}\n\nresource \"azurerm_kubernetes_cluster\" \"example\" {\n name = \"<cluster-name>\"\n location = \"<location>\"\n resource_group_name = \"<resource-group>\"\n dns_prefix = \"<dns-prefix>\"\n\n default_node_pool {\n name = \"system\"\n node_count = 1\n vm_size = \"Standard_DS2_v2\"\n }\n\n identity {\n type = \"SystemAssigned\"\n }\n\n microsoft_defender {\n log_analytics_workspace_id = azurerm_log_analytics_workspace.example.id\n }\n}\n```"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Enable the Microsoft Defender security profile for AKS clusters and ensure the subscription has Defender for Containers enabled. Route security monitoring data to the appropriate workspace and review Defender alerts and recommendations as part of the cluster operations process.",
|
||||
"Url": "https://hub.prowler.com/check/aks_cluster_defender_enabled"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"threat-detection",
|
||||
"cluster-security"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "This check evaluates the AKS managed cluster Defender security monitoring flag. Defender for Containers plan availability, billing, workspace routing, and alert response processes should be validated separately at the subscription and security operations levels."
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
from prowler.lib.check.models import Check, Check_Report_Azure
|
||||
from prowler.providers.azure.services.aks.aks_client import aks_client
|
||||
|
||||
|
||||
class aks_cluster_defender_enabled(Check):
|
||||
def execute(self) -> list[Check_Report_Azure]:
|
||||
findings = []
|
||||
|
||||
for subscription_name, clusters in aks_client.clusters.items():
|
||||
for cluster in clusters.values():
|
||||
report = Check_Report_Azure(metadata=self.metadata(), resource=cluster)
|
||||
report.subscription = subscription_name
|
||||
|
||||
if cluster.defender_enabled is True:
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"Cluster '{cluster.name}' has Defender for Containers "
|
||||
"enabled."
|
||||
)
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"Cluster '{cluster.name}' does not have Defender for "
|
||||
"Containers enabled."
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
from importlib import import_module
|
||||
from unittest import mock
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
|
||||
from prowler.providers.azure.services.aks.aks_service import Cluster
|
||||
from tests.providers.azure.azure_fixtures import (
|
||||
AZURE_SUBSCRIPTION_ID,
|
||||
set_mocked_azure_provider,
|
||||
)
|
||||
|
||||
CHECK_MODULE = (
|
||||
"prowler.providers.azure.services.aks.aks_cluster_defender_enabled."
|
||||
"aks_cluster_defender_enabled"
|
||||
)
|
||||
CHECK_CLIENT_PATCH = f"{CHECK_MODULE}.aks_client"
|
||||
|
||||
|
||||
def get_check_class():
|
||||
return import_module(CHECK_MODULE).aks_cluster_defender_enabled
|
||||
|
||||
|
||||
def build_cluster(defender_enabled):
|
||||
return Cluster(
|
||||
id=str(uuid4()),
|
||||
name="test-cluster",
|
||||
public_fqdn="test.azmk8s.io",
|
||||
private_fqdn=None,
|
||||
network_policy=None,
|
||||
agent_pool_profiles=[],
|
||||
rbac_enabled=True,
|
||||
location="eastus",
|
||||
defender_enabled=defender_enabled,
|
||||
)
|
||||
|
||||
|
||||
class Test_aks_cluster_defender_enabled:
|
||||
def test_no_subscriptions(self):
|
||||
aks_client = mock.MagicMock
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
CHECK_CLIENT_PATCH,
|
||||
new=aks_client,
|
||||
),
|
||||
):
|
||||
aks_cluster_defender_enabled = get_check_class()
|
||||
|
||||
aks_client.clusters = {}
|
||||
|
||||
check = aks_cluster_defender_enabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
def test_pass(self):
|
||||
aks_client = mock.MagicMock
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
CHECK_CLIENT_PATCH,
|
||||
new=aks_client,
|
||||
),
|
||||
):
|
||||
aks_cluster_defender_enabled = get_check_class()
|
||||
|
||||
cluster = build_cluster(defender_enabled=True)
|
||||
aks_client.clusters = {AZURE_SUBSCRIPTION_ID: {cluster.id: cluster}}
|
||||
|
||||
check = aks_cluster_defender_enabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "Cluster 'test-cluster' has Defender for Containers enabled."
|
||||
)
|
||||
assert result[0].resource_name == "test-cluster"
|
||||
assert result[0].resource_id == cluster.id
|
||||
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
|
||||
assert result[0].location == "eastus"
|
||||
|
||||
@pytest.mark.parametrize("defender_enabled", [False, None, "true"])
|
||||
def test_fail(self, defender_enabled):
|
||||
aks_client = mock.MagicMock
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
CHECK_CLIENT_PATCH,
|
||||
new=aks_client,
|
||||
),
|
||||
):
|
||||
aks_cluster_defender_enabled = get_check_class()
|
||||
|
||||
cluster = build_cluster(defender_enabled=defender_enabled)
|
||||
aks_client.clusters = {AZURE_SUBSCRIPTION_ID: {cluster.id: cluster}}
|
||||
|
||||
check = aks_cluster_defender_enabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "Cluster 'test-cluster' does not have Defender for "
|
||||
"Containers enabled."
|
||||
)
|
||||
assert result[0].resource_name == "test-cluster"
|
||||
assert result[0].resource_id == cluster.id
|
||||
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
|
||||
assert result[0].location == "eastus"
|
||||
Reference in New Issue
Block a user