feat(azure): new check sqlserver_vulnerability_assessment_enabled (#3349)

This commit is contained in:
Pedro Martín
2024-02-05 11:39:05 +01:00
committed by GitHub
parent 7c0ff1ff6a
commit de77f3ff13
8 changed files with 325 additions and 3 deletions

View File

@@ -6,6 +6,7 @@ from azure.mgmt.sql.models import (
FirewallRule,
ServerBlobAuditingPolicy,
ServerExternalAdministrator,
ServerVulnerabilityAssessment,
TransparentDataEncryption,
)
@@ -40,6 +41,9 @@ class SQLServer(AzureService):
encryption_protector = self.__get_enctyption_protectors__(
subscription, resource_group, sql_server.name
)
vulnerability_assessment = self.__get_vulnerability_assesments__(
subscription, resource_group, sql_server.name
)
sql_servers[subscription].append(
SQL_Server(
id=sql_server.id,
@@ -53,6 +57,7 @@ class SQLServer(AzureService):
databases=self.__get_databases__(
subscription, resource_group, sql_server.name
),
vulnerability_assessment=vulnerability_assessment,
)
)
except Exception as error:
@@ -115,6 +120,17 @@ class SQLServer(AzureService):
)
return databases
def __get_vulnerability_assesments__(
self, subscription, resource_group, server_name
):
client = self.clients[subscription]
vulnerability_assessment = client.server_vulnerability_assessments.get(
resource_group_name=resource_group,
server_name=server_name,
vulnerability_assessment_name="default",
)
return vulnerability_assessment
@dataclass
class DatabaseServer:
@@ -137,3 +153,4 @@ class SQL_Server:
firewall_rules: FirewallRule
encryption_protector: EncryptionProtector = None
databases: list[DatabaseServer] = None
vulnerability_assessment: ServerVulnerabilityAssessment = None

View File

@@ -0,0 +1,30 @@
{
"Provider": "azure",
"CheckID": "sqlserver_vulnerability_assessment_enabled",
"CheckTitle": "Ensure that Vulnerability Assessment (VA) is enabled on a SQL server by setting a Storage Account",
"CheckType": [],
"ServiceName": "sqlserver",
"SubServiceName": "",
"ResourceIdTemplate": "",
"Severity": "medium",
"ResourceType": "SQLServer",
"Description": "Enable Vulnerability Assessment (VA) service scans for critical SQL servers and corresponding SQL databases.",
"Risk": "The Vulnerability Assessment service scans databases for known security vulnerabilities and highlights deviations from best practices, such as misconfigurations, excessive permissions, and unprotected sensitive data. Results of the scan include actionable steps to resolve each issue and provide customized remediation scripts where applicable. Additionally, an assessment report can be customized by setting an acceptable baseline for permission configurations, feature configurations, and database settings.",
"RelatedUrl": "https://docs.microsoft.com/en-us/azure/sql-database/sql-vulnerability-assessment",
"Remediation": {
"Code": {
"CLI": "Update-AzSqlServerVulnerabilityAssessmentSetting -ResourceGroupName resource_group_name -ServerName Server_Name -StorageAccountName Storage_Name_from_same_subscription_and_same_Location -ScanResultsContainerName vulnerability-assessment -RecurringScansInterval Weekly -EmailSubscriptionAdmins $true -NotificationEmail @('mail1@mail.com' , 'mail2@mail.com')",
"NativeIaC": "",
"Other": "https://www.trendmicro.com/cloudoneconformity-staging/knowledge-base/azure/Sql/vulnerability-assessment-sql-servers.html#",
"Terraform": "https://docs.bridgecrew.io/docs/ensure-that-vulnerability-assessment-va-is-enabled-on-a-sql-server-by-setting-a-storage-account"
},
"Recommendation": {
"Text": "1. Go to SQL servers 2. Select a server instance 3. Click on Security Center 4. Select Configure next to Enabled at subscription-level 5. In Section Vulnerability Assessment Settings, Click Select Storage account 6. Choose Storage Account (Existing or Create New). Click Ok 7. Click Save",
"Url": "https://learn.microsoft.com/en-us/azure/defender-for-cloud/sql-azure-vulnerability-assessment-enable"
}
},
"Categories": [],
"DependsOn": [],
"RelatedTo": [],
"Notes": "Enabling the Microsoft Defender for SQL features will incur additional costs for each SQL server."
}

View File

@@ -0,0 +1,25 @@
from prowler.lib.check.models import Check, Check_Report_Azure
from prowler.providers.azure.services.sqlserver.sqlserver_client import sqlserver_client
class sqlserver_vulnerability_assessment_enabled(Check):
def execute(self) -> Check_Report_Azure:
findings = []
for subscription, sql_servers in sqlserver_client.sql_servers.items():
for sql_server in sql_servers:
report = Check_Report_Azure(self.metadata())
report.subscription = subscription
report.resource_name = sql_server.name
report.resource_id = sql_server.id
report.status = "FAIL"
report.status_extended = f"SQL Server {sql_server.name} from subscription {subscription} has vulnerability assessment disabled."
if (
sql_server.vulnerability_assessment
and sql_server.vulnerability_assessment.storage_container_path
is not None
):
report.status = "PASS"
report.status_extended = f"SQL Server {sql_server.name} from subscription {subscription} has vulnerability assessment enabled."
findings.append(report)
return findings

View File

@@ -115,6 +115,20 @@ expected_packages = [
name="prowler.providers.azure.services.sqlserver.sqlserver_auditing_retention_90_days.sqlserver_auditing_retention_90_days",
ispkg=False,
),
ModuleInfo(
module_finder=FileFinder(
"/root_dir/prowler/providers/azure/services/sqlserver"
),
name="prowler.providers.azure.services.sqlserver.sqlserver_vulnerability_assessment_enabled",
ispkg=True,
),
ModuleInfo(
module_finder=FileFinder(
"/root_dir/prowler/providers/azure/services/sqlserver/sqlserver_vulnerability_assessment_enabled"
),
name="prowler.providers.azure.services.sqlserver.sqlserver_vulnerability_assessment_enabled.sqlserver_vulnerability_assessment_enabled",
ispkg=False,
),
]
@@ -208,6 +222,20 @@ def mock_list_modules(*_):
name="prowler.providers.azure.services.sqlserver.sqlserver_auditing_retention_90_days.sqlserver_auditing_retention_90_days",
ispkg=False,
),
ModuleInfo(
module_finder=FileFinder(
"/root_dir/prowler/providers/azure/services/sqlserver"
),
name="prowler.providers.azure.services.sqlserver.sqlserver_vulnerability_assessment_enabled",
ispkg=True,
),
ModuleInfo(
module_finder=FileFinder(
"/root_dir/prowler/providers/azure/services/sqlserver/sqlserver_vulnerability_assessment_enabled"
),
name="prowler.providers.azure.services.sqlserver.sqlserver_vulnerability_assessment_enabled.sqlserver_vulnerability_assessment_enabled",
ispkg=False,
),
]
return modules
@@ -601,6 +629,10 @@ class Test_Check:
"sqlserver_auditing_retention_90_days",
"/root_dir/prowler/providers/azure/services/sqlserver/sqlserver_auditing_retention_90_days",
),
(
"sqlserver_vulnerability_assessment_enabled",
"/root_dir/prowler/providers/azure/services/sqlserver/sqlserver_vulnerability_assessment_enabled",
),
]
returned_checks = recover_checks_from_provider(provider, service)
assert returned_checks == expected_checks

View File

@@ -8,8 +8,7 @@ from azure.mgmt.sql.models import (
)
from prowler.providers.azure.services.sqlserver.sqlserver_service import SQL_Server
AZURE_SUSCRIPTION = str(uuid4())
from tests.providers.azure.azure_fixtures import AZURE_SUSCRIPTION
class Test_sqlserver_auditing_enabled:

View File

@@ -1,6 +1,10 @@
from unittest.mock import patch
from azure.mgmt.sql.models import EncryptionProtector, TransparentDataEncryption
from azure.mgmt.sql.models import (
EncryptionProtector,
ServerVulnerabilityAssessment,
TransparentDataEncryption,
)
from prowler.providers.azure.services.sqlserver.sqlserver_service import (
DatabaseServer,
@@ -36,6 +40,9 @@ def mock_sqlserver_get_sql_servers(_):
server_key_type="AzureKeyVault"
),
databases=[database],
vulnerability_assessment=ServerVulnerabilityAssessment(
storage_container_path="/subcription_id/resource_group/sql_server"
),
)
]
}
@@ -87,6 +94,12 @@ class Test_SqlServer_Service:
== "EncryptionProtector"
)
assert sql_server.sql_servers[AZURE_SUSCRIPTION][0].databases == [database]
assert (
sql_server.sql_servers[AZURE_SUSCRIPTION][
0
].vulnerability_assessment.__class__.__name__
== "ServerVulnerabilityAssessment"
)
def test__get_databases__(self):
sql_server = SQLServer(set_mocked_azure_audit_info())
@@ -146,3 +159,19 @@ class Test_SqlServer_Service:
id = "/subscriptions/subscription_id/resourceGroups/resource_group/providers/Microsoft.Sql/servers/sql_server"
sql_server = SQLServer(set_mocked_azure_audit_info())
assert sql_server.__get_resource_group__(id) == "resource_group"
def test__get_vulnerability_assessment__(self):
sql_server = SQLServer(set_mocked_azure_audit_info())
storage_container_path = "/subcription_id/resource_group/sql_server"
assert (
sql_server.sql_servers[AZURE_SUSCRIPTION][
0
].vulnerability_assessment.__class__.__name__
== "ServerVulnerabilityAssessment"
)
assert (
sql_server.sql_servers[AZURE_SUSCRIPTION][
0
].vulnerability_assessment.storage_container_path
== storage_container_path
)

View File

@@ -0,0 +1,190 @@
from unittest import mock
from uuid import uuid4
from azure.mgmt.sql.models import (
EncryptionProtector,
ServerVulnerabilityAssessment,
TransparentDataEncryption,
)
from prowler.providers.azure.services.sqlserver.sqlserver_service import (
DatabaseServer,
SQL_Server,
)
AZURE_SUSCRIPTION = str(uuid4())
class Test_sqlserver_vulnerability_assessment_enabled:
def test_no_sql_servers(self):
sqlserver_client = mock.MagicMock
sqlserver_client.sql_servers = {}
with mock.patch(
"prowler.providers.azure.services.sqlserver.sqlserver_vulnerability_assessment_enabled.sqlserver_vulnerability_assessment_enabled.sqlserver_client",
new=sqlserver_client,
):
from prowler.providers.azure.services.sqlserver.sqlserver_vulnerability_assessment_enabled.sqlserver_vulnerability_assessment_enabled import (
sqlserver_vulnerability_assessment_enabled,
)
check = sqlserver_vulnerability_assessment_enabled()
result = check.execute()
assert len(result) == 0
def test_sql_servers_no_vulnerability_assessment(self):
sqlserver_client = mock.MagicMock
sql_server_name = "SQL Server Name"
sql_server_id = str(uuid4())
database = DatabaseServer(
id="id",
name="name",
type="type",
location="location",
managed_by="managed_by",
tde_encryption=None,
)
sqlserver_client.sql_servers = {
AZURE_SUSCRIPTION: [
SQL_Server(
id=sql_server_id,
name=sql_server_name,
public_network_access="",
minimal_tls_version="",
administrators=None,
auditing_policies=None,
firewall_rules=None,
databases=[database],
encryption_protector=EncryptionProtector(
server_key_type="ServiceManaged"
),
vulnerability_assessment=None,
)
]
}
with mock.patch(
"prowler.providers.azure.services.sqlserver.sqlserver_vulnerability_assessment_enabled.sqlserver_vulnerability_assessment_enabled.sqlserver_client",
new=sqlserver_client,
):
from prowler.providers.azure.services.sqlserver.sqlserver_vulnerability_assessment_enabled.sqlserver_vulnerability_assessment_enabled import (
sqlserver_vulnerability_assessment_enabled,
)
check = sqlserver_vulnerability_assessment_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"SQL Server {sql_server_name} from subscription {AZURE_SUSCRIPTION} has vulnerability assessment disabled."
)
assert result[0].subscription == AZURE_SUSCRIPTION
assert result[0].resource_name == sql_server_name
assert result[0].resource_id == sql_server_id
def test_sql_servers_no_vulnerability_assessment_path(self):
sqlserver_client = mock.MagicMock
sql_server_name = "SQL Server Name"
sql_server_id = str(uuid4())
database = DatabaseServer(
id="id",
name="name",
type="type",
location="location",
managed_by="managed_by",
tde_encryption=TransparentDataEncryption(status="Disabled"),
)
sqlserver_client.sql_servers = {
AZURE_SUSCRIPTION: [
SQL_Server(
id=sql_server_id,
name=sql_server_name,
public_network_access="",
minimal_tls_version="",
administrators=None,
auditing_policies=None,
firewall_rules=None,
databases=[database],
encryption_protector=EncryptionProtector(
server_key_type="AzureKeyVault"
),
vulnerability_assessment=ServerVulnerabilityAssessment(
storage_container_path=None
),
)
]
}
with mock.patch(
"prowler.providers.azure.services.sqlserver.sqlserver_vulnerability_assessment_enabled.sqlserver_vulnerability_assessment_enabled.sqlserver_client",
new=sqlserver_client,
):
from prowler.providers.azure.services.sqlserver.sqlserver_vulnerability_assessment_enabled.sqlserver_vulnerability_assessment_enabled import (
sqlserver_vulnerability_assessment_enabled,
)
check = sqlserver_vulnerability_assessment_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"SQL Server {sql_server_name} from subscription {AZURE_SUSCRIPTION} has vulnerability assessment disabled."
)
assert result[0].subscription == AZURE_SUSCRIPTION
assert result[0].resource_name == sql_server_name
assert result[0].resource_id == sql_server_id
def test_sql_servers_vulnerability_assessment_enabled(self):
sqlserver_client = mock.MagicMock
sql_server_name = "SQL Server Name"
sql_server_id = str(uuid4())
database = DatabaseServer(
id="id",
name="name",
type="type",
location="location",
managed_by="managed_by",
tde_encryption=TransparentDataEncryption(status="Enabled"),
)
sqlserver_client.sql_servers = {
AZURE_SUSCRIPTION: [
SQL_Server(
id=sql_server_id,
name=sql_server_name,
public_network_access="",
minimal_tls_version="",
administrators=None,
auditing_policies=None,
firewall_rules=None,
databases=[database],
encryption_protector=EncryptionProtector(
server_key_type="AzureKeyVault"
),
vulnerability_assessment=ServerVulnerabilityAssessment(
storage_container_path="/subcription_id/resource_group/sql_server"
),
)
]
}
with mock.patch(
"prowler.providers.azure.services.sqlserver.sqlserver_vulnerability_assessment_enabled.sqlserver_vulnerability_assessment_enabled.sqlserver_client",
new=sqlserver_client,
):
from prowler.providers.azure.services.sqlserver.sqlserver_vulnerability_assessment_enabled.sqlserver_vulnerability_assessment_enabled import (
sqlserver_vulnerability_assessment_enabled,
)
check = sqlserver_vulnerability_assessment_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"SQL Server {sql_server_name} from subscription {AZURE_SUSCRIPTION} has vulnerability assessment enabled."
)
assert result[0].subscription == AZURE_SUSCRIPTION
assert result[0].resource_name == sql_server_name
assert result[0].resource_id == sql_server_id