feat(storage): add new check storage_smb_protocol_version_is_latest (#8128)

Co-authored-by: Sergio Garcia <hello@mistercloudsec.com>
This commit is contained in:
Rubén De la Torre Vico
2025-07-09 11:28:00 +02:00
committed by GitHub
parent 15a8671f0d
commit a319f80701
11 changed files with 428 additions and 7 deletions
+1
View File
@@ -6,6 +6,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
### Added
- `storage_smb_channel_encryption_with_secure_algorithm` check for Azure provider [(#8123)](https://github.com/prowler-cloud/prowler/pull/8123)
- `storage_smb_protocol_version_is_latest` check for Azure provider [(#8128)](https://github.com/prowler-cloud/prowler/pull/8128)
- `vm_backup_enabled` check for Azure provider [(#8182)](https://github.com/prowler-cloud/prowler/pull/8182)
- `vm_linux_enforce_ssh_authentication` check for Azure provider [(#8149)](https://github.com/prowler-cloud/prowler/pull/8149)
- `vm_ensure_using_approved_images` check for Azure provider [(#8168)](https://github.com/prowler-cloud/prowler/pull/8168)
@@ -0,0 +1,3 @@
"""Constants for the storage service."""
LATEST_SMB_VERSION = "SMB3.1.1"
@@ -172,6 +172,20 @@ class Storage(AzureService):
None,
)
smb_supported_versions_raw = getattr(
getattr(
getattr(
file_service_properties,
"protocol_settings",
None,
),
"smb",
None,
),
"versions",
None,
)
account.file_service_properties = FileServiceProperties(
id=file_service_properties.id,
name=file_service_properties.name,
@@ -193,7 +207,12 @@ class Storage(AzureService):
smb_channel_encryption_raw.rstrip(";").split(";")
if smb_channel_encryption_raw
else []
)
),
supported_versions=(
smb_supported_versions_raw.rstrip(";").split(";")
if smb_supported_versions_raw
else []
),
),
)
except Exception as error:
@@ -240,6 +259,7 @@ class ReplicationSettings(Enum):
class SMBProtocolSettings(BaseModel):
channel_encryption: list[str]
supported_versions: list[str]
class FileServiceProperties(BaseModel):
@@ -0,0 +1,30 @@
{
"Provider": "azure",
"CheckID": "storage_smb_protocol_version_is_latest",
"CheckTitle": "Ensure SMB protocol version for file shares is set to the latest version.",
"CheckType": [],
"ServiceName": "storage",
"SubServiceName": "",
"ResourceIdTemplate": "/subscriptions/{subscription_id}/resourceGroups/{resource_group}/providers/Microsoft.Storage/storageAccounts/{storageAccountName}/fileServices/default",
"Severity": "medium",
"ResourceType": "AzureStorageAccount",
"Description": "Ensure that SMB file shares are configured to use only the latest SMB protocol version.",
"Risk": "Allowing older SMB protocol versions may expose file shares to known vulnerabilities and security risks.",
"RelatedUrl": "https://learn.microsoft.com/en-us/azure/storage/files/files-smb-protocol#smb-security-settings",
"Remediation": {
"Code": {
"CLI": "az storage account file-service-properties update --resource-group <resource-group> --account-name <storage-account> --versions <latest-version>",
"NativeIaC": "",
"Other": "",
"Terraform": ""
},
"Recommendation": {
"Text": "Configure your Azure Storage Account file shares to allow only the latest SMB protocol version.",
"Url": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/azure/StorageAccounts/latest-smb-protocol-version.html"
}
},
"Categories": [],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
@@ -0,0 +1,48 @@
from prowler.lib.check.models import Check, Check_Report_Azure
from prowler.providers.azure.services.storage.lib.constants import LATEST_SMB_VERSION
from prowler.providers.azure.services.storage.storage_client import storage_client
class storage_smb_protocol_version_is_latest(Check):
"""
Ensure SMB protocol version for file shares is set to the latest version.
This check evaluates whether SMB file shares are configured to use only the latest SMB protocol version.
- PASS: Storage account allows only the latest SMB protocol version for file shares.
- FAIL: Storage account allows other SMB protocol versions for file shares.
"""
def execute(self) -> list[Check_Report_Azure]:
findings = []
for subscription, storage_accounts in storage_client.storage_accounts.items():
for account in storage_accounts:
if getattr(account, "file_service_properties", None) and getattr(
account.file_service_properties.smb_protocol_settings,
"supported_versions",
None,
):
report = Check_Report_Azure(
metadata=self.metadata(),
resource=account.file_service_properties,
)
report.subscription = subscription
report.resource_name = account.name
report.location = account.location
if (
len(
account.file_service_properties.smb_protocol_settings.supported_versions
)
== 1
and account.file_service_properties.smb_protocol_settings.supported_versions[
0
]
== LATEST_SMB_VERSION
):
report.status = "PASS"
report.status_extended = f"Storage account {account.name} from subscription {subscription} allows only the latest SMB protocol version ({LATEST_SMB_VERSION}) for file shares."
else:
report.status = "FAIL"
report.status_extended = f"Storage account {account.name} from subscription {subscription} allows SMB protocol versions: {', '.join(account.file_service_properties.smb_protocol_settings.supported_versions) if account.file_service_properties.smb_protocol_settings.supported_versions else 'None'}. Only the latest SMB protocol version ({LATEST_SMB_VERSION}) should be allowed."
findings.append(report)
return findings
@@ -91,7 +91,9 @@ class Test_storage_ensure_file_shares_soft_delete_is_enabled:
name="default",
type="Microsoft.Storage/storageAccounts/fileServices",
share_delete_retention_policy=retention_policy,
smb_protocol_settings=SMBProtocolSettings(channel_encryption=[]),
smb_protocol_settings=SMBProtocolSettings(
channel_encryption=[], supported_versions=[]
),
)
storage_client.storage_accounts = {
AZURE_SUBSCRIPTION_ID: [
@@ -152,7 +154,9 @@ class Test_storage_ensure_file_shares_soft_delete_is_enabled:
name="default",
type="Microsoft.Storage/storageAccounts/fileServices",
share_delete_retention_policy=retention_policy,
smb_protocol_settings=SMBProtocolSettings(channel_encryption=[]),
smb_protocol_settings=SMBProtocolSettings(
channel_encryption=[], supported_versions=[]
),
)
storage_client.storage_accounts = {
AZURE_SUBSCRIPTION_ID: [
@@ -30,7 +30,9 @@ def mock_storage_get_storage_accounts(_):
name="name",
type="type",
share_delete_retention_policy=retention_policy,
smb_protocol_settings=SMBProtocolSettings(channel_encryption=[]),
smb_protocol_settings=SMBProtocolSettings(
channel_encryption=[], supported_versions=[]
),
)
return {
AZURE_SUBSCRIPTION_ID: [
@@ -215,3 +217,11 @@ class Test_Storage_Service:
is True
)
assert account.file_service_properties.share_delete_retention_policy.days == 7
assert (
account.file_service_properties.smb_protocol_settings.channel_encryption
== []
)
assert (
account.file_service_properties.smb_protocol_settings.supported_versions
== []
)
@@ -87,7 +87,9 @@ class Test_storage_smb_channel_encryption_with_secure_algorithm:
name="fs1",
type="type1",
share_delete_retention_policy=DeleteRetentionPolicy(enabled=True, days=7),
smb_protocol_settings=SMBProtocolSettings(channel_encryption=[]),
smb_protocol_settings=SMBProtocolSettings(
channel_encryption=[], supported_versions=[]
),
)
storage_client = mock.MagicMock()
storage_client.storage_accounts = {
@@ -142,7 +144,7 @@ class Test_storage_smb_channel_encryption_with_secure_algorithm:
type="type1",
share_delete_retention_policy=DeleteRetentionPolicy(enabled=True, days=7),
smb_protocol_settings=SMBProtocolSettings(
channel_encryption=["AES-128-GCM"]
channel_encryption=["AES-128-GCM"], supported_versions=[]
),
)
storage_client = mock.MagicMock()
@@ -198,7 +200,7 @@ class Test_storage_smb_channel_encryption_with_secure_algorithm:
type="type1",
share_delete_retention_policy=DeleteRetentionPolicy(enabled=True, days=7),
smb_protocol_settings=SMBProtocolSettings(
channel_encryption=["AES-256-GCM"]
channel_encryption=["AES-256-GCM"], supported_versions=[]
),
)
storage_client = mock.MagicMock()
@@ -0,0 +1,303 @@
from unittest import mock
from uuid import uuid4
from prowler.providers.azure.services.storage.storage_service import (
Account,
DeleteRetentionPolicy,
FileServiceProperties,
NetworkRuleSet,
SMBProtocolSettings,
)
from tests.providers.azure.azure_fixtures import (
AZURE_SUBSCRIPTION_ID,
set_mocked_azure_provider,
)
class Test_storage_smb_protocol_version_is_latest:
def test_no_storage_accounts(self):
storage_client = mock.MagicMock()
storage_client.storage_accounts = {}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.storage.storage_smb_protocol_version_is_latest.storage_smb_protocol_version_is_latest.storage_client",
new=storage_client,
),
):
from prowler.providers.azure.services.storage.storage_smb_protocol_version_is_latest.storage_smb_protocol_version_is_latest import (
storage_smb_protocol_version_is_latest,
)
check = storage_smb_protocol_version_is_latest()
result = check.execute()
assert len(result) == 0
def test_no_file_service_properties(self):
storage_account_id = str(uuid4())
storage_account_name = "Test Storage Account"
storage_client = mock.MagicMock()
storage_client.storage_accounts = {
AZURE_SUBSCRIPTION_ID: [
Account(
id=storage_account_id,
name=storage_account_name,
resouce_group_name="rg",
enable_https_traffic_only=True,
infrastructure_encryption=True,
allow_blob_public_access=False,
network_rule_set=NetworkRuleSet(
bypass="AzureServices", default_action="Allow"
),
encryption_type="type",
minimum_tls_version="TLS1_2",
private_endpoint_connections=[],
key_expiration_period_in_days=None,
location="eastus",
file_service_properties=None,
)
]
}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.storage.storage_smb_protocol_version_is_latest.storage_smb_protocol_version_is_latest.storage_client",
new=storage_client,
),
):
from prowler.providers.azure.services.storage.storage_smb_protocol_version_is_latest.storage_smb_protocol_version_is_latest import (
storage_smb_protocol_version_is_latest,
)
check = storage_smb_protocol_version_is_latest()
result = check.execute()
assert len(result) == 0
def test_only_latest_smb_protocol_version(self):
storage_account_id = str(uuid4())
storage_account_name = "Test Storage Account"
file_service_properties = FileServiceProperties(
id=storage_account_id,
name="default",
type="type",
share_delete_retention_policy=DeleteRetentionPolicy(enabled=True, days=7),
smb_protocol_settings=SMBProtocolSettings(
channel_encryption=[], supported_versions=["SMB3.1.1"]
),
)
storage_client = mock.MagicMock()
storage_client.storage_accounts = {
AZURE_SUBSCRIPTION_ID: [
Account(
id=storage_account_id,
name=storage_account_name,
resouce_group_name="rg",
enable_https_traffic_only=True,
infrastructure_encryption=True,
allow_blob_public_access=False,
network_rule_set=NetworkRuleSet(
bypass="AzureServices", default_action="Allow"
),
encryption_type="type",
minimum_tls_version="TLS1_2",
private_endpoint_connections=[],
key_expiration_period_in_days=None,
location="eastus",
file_service_properties=file_service_properties,
)
]
}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.storage.storage_smb_protocol_version_is_latest.storage_smb_protocol_version_is_latest.storage_client",
new=storage_client,
),
):
from prowler.providers.azure.services.storage.storage_smb_protocol_version_is_latest.storage_smb_protocol_version_is_latest import (
storage_smb_protocol_version_is_latest,
)
check = storage_smb_protocol_version_is_latest()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
f"Storage account {storage_account_name} from subscription {AZURE_SUBSCRIPTION_ID} allows only the latest SMB protocol version (SMB3.1.1) for file shares."
in result[0].status_extended
)
def test_multiple_smb_protocol_versions(self):
storage_account_id = str(uuid4())
storage_account_name = "Test Storage Account"
file_service_properties = FileServiceProperties(
id=storage_account_id,
name="default",
type="type",
share_delete_retention_policy=DeleteRetentionPolicy(enabled=True, days=7),
smb_protocol_settings=SMBProtocolSettings(
channel_encryption=[], supported_versions=["SMB2.1", "SMB3.1.1"]
),
)
storage_client = mock.MagicMock()
storage_client.storage_accounts = {
AZURE_SUBSCRIPTION_ID: [
Account(
id=storage_account_id,
name=storage_account_name,
resouce_group_name="rg",
enable_https_traffic_only=True,
infrastructure_encryption=True,
allow_blob_public_access=False,
network_rule_set=NetworkRuleSet(
bypass="AzureServices", default_action="Allow"
),
encryption_type="type",
minimum_tls_version="TLS1_2",
private_endpoint_connections=[],
key_expiration_period_in_days=None,
location="eastus",
file_service_properties=file_service_properties,
)
]
}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.storage.storage_smb_protocol_version_is_latest.storage_smb_protocol_version_is_latest.storage_client",
new=storage_client,
),
):
from prowler.providers.azure.services.storage.storage_smb_protocol_version_is_latest.storage_smb_protocol_version_is_latest import (
storage_smb_protocol_version_is_latest,
)
check = storage_smb_protocol_version_is_latest()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
f"Storage account {storage_account_name} from subscription {AZURE_SUBSCRIPTION_ID} allows SMB protocol versions: SMB2.1, SMB3.1.1. Only the latest SMB protocol version (SMB3.1.1) should be allowed."
in result[0].status_extended
)
def test_no_smb_protocol_versions(self):
storage_account_id = str(uuid4())
storage_account_name = "Test Storage Account"
file_service_properties = FileServiceProperties(
id=storage_account_id,
name="default",
type="type",
share_delete_retention_policy=DeleteRetentionPolicy(enabled=True, days=7),
smb_protocol_settings=SMBProtocolSettings(
channel_encryption=[], supported_versions=[]
),
)
storage_client = mock.MagicMock()
storage_client.storage_accounts = {
AZURE_SUBSCRIPTION_ID: [
Account(
id=storage_account_id,
name=storage_account_name,
resouce_group_name="rg",
enable_https_traffic_only=True,
infrastructure_encryption=True,
allow_blob_public_access=False,
network_rule_set=NetworkRuleSet(
bypass="AzureServices", default_action="Allow"
),
encryption_type="type",
minimum_tls_version="TLS1_2",
private_endpoint_connections=[],
key_expiration_period_in_days=None,
location="eastus",
file_service_properties=file_service_properties,
)
]
}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.storage.storage_smb_protocol_version_is_latest.storage_smb_protocol_version_is_latest.storage_client",
new=storage_client,
),
):
from prowler.providers.azure.services.storage.storage_smb_protocol_version_is_latest.storage_smb_protocol_version_is_latest import (
storage_smb_protocol_version_is_latest,
)
check = storage_smb_protocol_version_is_latest()
result = check.execute()
assert len(result) == 0
def test_multiple_required_versions_custom_config(self):
storage_account_id = str(uuid4())
storage_account_name = "Test Storage Account"
file_service_properties = FileServiceProperties(
id=storage_account_id,
name="default",
type="type",
share_delete_retention_policy=DeleteRetentionPolicy(enabled=True, days=7),
smb_protocol_settings=SMBProtocolSettings(
channel_encryption=[], supported_versions=["SMB3.1.1", "SMB3.0"]
),
)
storage_client = mock.MagicMock()
storage_client.storage_accounts = {
AZURE_SUBSCRIPTION_ID: [
Account(
id=storage_account_id,
name=storage_account_name,
resouce_group_name="rg",
enable_https_traffic_only=True,
infrastructure_encryption=True,
allow_blob_public_access=False,
network_rule_set=NetworkRuleSet(
bypass="AzureServices", default_action="Allow"
),
encryption_type="type",
minimum_tls_version="TLS1_2",
private_endpoint_connections=[],
key_expiration_period_in_days=None,
location="eastus",
file_service_properties=file_service_properties,
)
]
}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.storage.storage_smb_protocol_version_is_latest.storage_smb_protocol_version_is_latest.storage_client",
new=storage_client,
),
):
from prowler.providers.azure.services.storage.storage_smb_protocol_version_is_latest.storage_smb_protocol_version_is_latest import (
storage_smb_protocol_version_is_latest,
)
check = storage_smb_protocol_version_is_latest()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
f"Storage account {storage_account_name} from subscription {AZURE_SUBSCRIPTION_ID} allows SMB protocol versions: SMB3.1.1, SMB3.0. Only the latest SMB protocol version (SMB3.1.1) should be allowed."
in result[0].status_extended
)