feat(azure): new check for Entra ID authentication for Azure PostgreSQL Flexible Server (#8764)

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Sergio Garcia <hello@mistercloudsec.com>
Co-authored-by: HugoPBrito <hugopbrit@gmail.com>
This commit is contained in:
johannes-engler-mw
2025-11-14 13:54:57 +01:00
committed by GitHub
parent 031548ca7e
commit 531ba5c31b
16 changed files with 437 additions and 35 deletions

View File

@@ -50,14 +50,16 @@ class Test_postgresql_flexible_server_allow_access_services_disabled:
id=postgresql_server_id,
name=postgresql_server_name,
resource_group="resource_group",
location="location",
require_secure_transport="OFF",
active_directory_auth=None,
entra_id_admins=[],
log_checkpoints="OFF",
log_connections="OFF",
log_disconnections="OFF",
connection_throttling="OFF",
log_retention_days="3",
firewall=[firewall],
location="location",
)
]
}
@@ -105,14 +107,16 @@ class Test_postgresql_flexible_server_allow_access_services_disabled:
id=postgresql_server_id,
name=postgresql_server_name,
resource_group="resource_group",
location="location",
require_secure_transport="OFF",
active_directory_auth=None,
entra_id_admins=[],
log_checkpoints="OFF",
log_connections="OFF",
log_disconnections="OFF",
connection_throttling="OFF",
log_retention_days="3",
firewall=[firewall],
location="location",
)
]
}

View File

@@ -41,14 +41,16 @@ class Test_postgresql_flexible_server_connection_throttling_on:
id=postgresql_server_id,
name=postgresql_server_name,
resource_group="resource_group",
location="location",
require_secure_transport="OFF",
active_directory_auth=None,
entra_id_admins=[],
log_checkpoints="OFF",
log_connections="OFF",
log_disconnections="OFF",
connection_throttling="OFF",
log_retention_days="3",
firewall=None,
location="location",
)
]
}
@@ -90,14 +92,16 @@ class Test_postgresql_flexible_server_connection_throttling_on:
id=postgresql_server_id,
name=postgresql_server_name,
resource_group="resource_group",
location="location",
require_secure_transport="OFF",
active_directory_auth=None,
entra_id_admins=[],
log_checkpoints="ON",
log_connections="ON",
log_disconnections="ON",
connection_throttling="ON",
log_retention_days="3",
firewall=None,
location="location",
)
]
}

View File

@@ -41,14 +41,16 @@ class Test_postgresql_flexible_server_enforce_ssl_enabled:
id=postgresql_server_id,
name=postgresql_server_name,
resource_group="resource_group",
location="location",
require_secure_transport="OFF",
active_directory_auth=None,
entra_id_admins=[],
log_checkpoints="ON",
log_connections="ON",
log_disconnections="ON",
connection_throttling="ON",
log_retention_days="3",
firewall=None,
location="location",
)
]
}
@@ -90,14 +92,16 @@ class Test_postgresql_flexible_server_enforce_ssl_enabled:
id=postgresql_server_id,
name=postgresql_server_name,
resource_group="resource_group",
location="location",
require_secure_transport="ON",
active_directory_auth=None,
entra_id_admins=[],
log_checkpoints="ON",
log_connections="ON",
log_disconnections="ON",
connection_throttling="ON",
log_retention_days="3",
firewall=None,
location="location",
)
]
}

View File

@@ -0,0 +1,195 @@
from unittest import mock
from uuid import uuid4
from prowler.providers.azure.services.postgresql.postgresql_service import (
EntraIdAdmin,
Server,
)
from tests.providers.azure.azure_fixtures import (
AZURE_SUBSCRIPTION_ID,
set_mocked_azure_provider,
)
class Test_postgresql_flexible_server_entra_id_authentication_enabled:
def test_no_postgresql_flexible_servers(self):
postgresql_client = mock.MagicMock
postgresql_client.flexible_servers = {}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.postgresql.postgresql_flexible_server_entra_id_authentication_enabled.postgresql_flexible_server_entra_id_authentication_enabled.postgresql_client",
new=postgresql_client,
),
):
from prowler.providers.azure.services.postgresql.postgresql_flexible_server_entra_id_authentication_enabled.postgresql_flexible_server_entra_id_authentication_enabled import (
postgresql_flexible_server_entra_id_authentication_enabled,
)
check = postgresql_flexible_server_entra_id_authentication_enabled()
result = check.execute()
assert len(result) == 0
def test_flexible_servers_entra_id_auth_disabled(self):
postgresql_client = mock.MagicMock
postgresql_server_name = "Postgres Flexible Server Name"
postgresql_server_id = str(uuid4())
postgresql_client.flexible_servers = {
AZURE_SUBSCRIPTION_ID: [
Server(
id=postgresql_server_id,
name=postgresql_server_name,
resource_group="resource_group",
location="location",
require_secure_transport="ON",
active_directory_auth="DISABLED",
entra_id_admins=[],
log_checkpoints="ON",
log_connections="ON",
log_disconnections="ON",
connection_throttling="ON",
log_retention_days="3",
firewall=None,
)
]
}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.postgresql.postgresql_flexible_server_entra_id_authentication_enabled.postgresql_flexible_server_entra_id_authentication_enabled.postgresql_client",
new=postgresql_client,
),
):
from prowler.providers.azure.services.postgresql.postgresql_flexible_server_entra_id_authentication_enabled.postgresql_flexible_server_entra_id_authentication_enabled import (
postgresql_flexible_server_entra_id_authentication_enabled,
)
check = postgresql_flexible_server_entra_id_authentication_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"Flexible Postgresql server {postgresql_server_name} from subscription {AZURE_SUBSCRIPTION_ID} has Microsoft Entra ID authentication disabled"
)
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
assert result[0].resource_name == postgresql_server_name
assert result[0].resource_id == postgresql_server_id
assert result[0].location == "location"
def test_flexible_servers_entra_id_auth_enabled_no_admins(self):
postgresql_client = mock.MagicMock
postgresql_server_name = "Postgres Flexible Server Name"
postgresql_server_id = str(uuid4())
postgresql_client.flexible_servers = {
AZURE_SUBSCRIPTION_ID: [
Server(
id=postgresql_server_id,
name=postgresql_server_name,
resource_group="resource_group",
location="location",
require_secure_transport="ON",
active_directory_auth="ENABLED",
entra_id_admins=[],
log_checkpoints="ON",
log_connections="ON",
log_disconnections="ON",
connection_throttling="ON",
log_retention_days="3",
firewall=None,
)
]
}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.postgresql.postgresql_flexible_server_entra_id_authentication_enabled.postgresql_flexible_server_entra_id_authentication_enabled.postgresql_client",
new=postgresql_client,
),
):
from prowler.providers.azure.services.postgresql.postgresql_flexible_server_entra_id_authentication_enabled.postgresql_flexible_server_entra_id_authentication_enabled import (
postgresql_flexible_server_entra_id_authentication_enabled,
)
check = postgresql_flexible_server_entra_id_authentication_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"Flexible Postgresql server {postgresql_server_name} from subscription {AZURE_SUBSCRIPTION_ID} has Microsoft Entra ID authentication enabled but no Entra ID administrators configured"
)
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
assert result[0].resource_name == postgresql_server_name
assert result[0].resource_id == postgresql_server_id
assert result[0].location == "location"
def test_flexible_servers_entra_id_auth_enabled(self):
postgresql_client = mock.MagicMock
postgresql_server_name = "Postgres Flexible Server Name"
postgresql_server_id = str(uuid4())
postgresql_client.flexible_servers = {
AZURE_SUBSCRIPTION_ID: [
Server(
id=postgresql_server_id,
name=postgresql_server_name,
resource_group="resource_group",
location="location",
require_secure_transport="ON",
active_directory_auth="ENABLED",
entra_id_admins=[
EntraIdAdmin(
object_id=str(uuid4()),
principal_name="Test Admin User",
principal_type="User",
tenant_id=str(uuid4()),
)
],
log_checkpoints="ON",
log_connections="ON",
log_disconnections="ON",
connection_throttling="ON",
log_retention_days="3",
firewall=None,
)
]
}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.postgresql.postgresql_flexible_server_entra_id_authentication_enabled.postgresql_flexible_server_entra_id_authentication_enabled.postgresql_client",
new=postgresql_client,
),
):
from prowler.providers.azure.services.postgresql.postgresql_flexible_server_entra_id_authentication_enabled.postgresql_flexible_server_entra_id_authentication_enabled import (
postgresql_flexible_server_entra_id_authentication_enabled,
)
check = postgresql_flexible_server_entra_id_authentication_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"Flexible Postgresql server {postgresql_server_name} from subscription {AZURE_SUBSCRIPTION_ID} has Microsoft Entra ID authentication enabled with 1 administrator configured"
)
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
assert result[0].resource_name == postgresql_server_name
assert result[0].resource_id == postgresql_server_id
assert result[0].location == "location"

View File

@@ -41,14 +41,16 @@ class Test_postgresql_flexible_server_log_checkpoints_on:
id=postgresql_server_id,
name=postgresql_server_name,
resource_group="resource_group",
location="location",
require_secure_transport="ON",
active_directory_auth=None,
entra_id_admins=[],
log_checkpoints="OFF",
log_connections="ON",
log_disconnections="ON",
connection_throttling="ON",
log_retention_days="3",
firewall=None,
location="location",
)
]
}
@@ -90,14 +92,16 @@ class Test_postgresql_flexible_server_log_checkpoints_on:
id=postgresql_server_id,
name=postgresql_server_name,
resource_group="resource_group",
location="location",
require_secure_transport="ON",
active_directory_auth=None,
entra_id_admins=[],
log_checkpoints="ON",
log_connections="ON",
log_disconnections="ON",
connection_throttling="ON",
log_retention_days="3",
firewall=None,
location="location",
)
]
}

View File

@@ -41,14 +41,16 @@ class Test_postgresql_flexible_server_log_connections_on:
id=postgresql_server_id,
name=postgresql_server_name,
resource_group="resource_group",
location="location",
require_secure_transport="OFF",
active_directory_auth=None,
entra_id_admins=[],
log_checkpoints="OFF",
log_connections="OFF",
log_disconnections="OFF",
connection_throttling="ON",
log_retention_days="3",
firewall=None,
location="location",
)
]
}
@@ -90,14 +92,16 @@ class Test_postgresql_flexible_server_log_connections_on:
id=postgresql_server_id,
name=postgresql_server_name,
resource_group="resource_group",
location="location",
require_secure_transport="OFF",
active_directory_auth=None,
entra_id_admins=[],
log_checkpoints="ON",
log_connections="ON",
log_disconnections="ON",
connection_throttling="ON",
log_retention_days="3",
firewall=None,
location="location",
)
]
}

View File

@@ -41,14 +41,16 @@ class Test_postgresql_flexible_server_log_disconnections_on:
id=postgresql_server_id,
name=postgresql_server_name,
resource_group="resource_group",
location="location",
require_secure_transport="OFF",
active_directory_auth=None,
entra_id_admins=[],
log_checkpoints="OFF",
log_connections="OFF",
log_disconnections="OFF",
connection_throttling="OFF",
log_retention_days="3",
firewall=None,
location="location",
)
]
}
@@ -90,14 +92,16 @@ class Test_postgresql_flexible_server_log_disconnections_on:
id=postgresql_server_id,
name=postgresql_server_name,
resource_group="resource_group",
location="location",
require_secure_transport="OFF",
active_directory_auth=None,
entra_id_admins=[],
log_checkpoints="ON",
log_connections="ON",
log_disconnections="ON",
connection_throttling="ON",
log_retention_days="3",
firewall=None,
location="location",
)
]
}

View File

@@ -41,14 +41,16 @@ class Test_postgresql_flexible_server_log_retention_days_greater_3:
id=postgresql_server_id,
name=postgresql_server_name,
resource_group="resource_group",
location="location",
require_secure_transport="OFF",
active_directory_auth=None,
entra_id_admins=[],
log_checkpoints="OFF",
log_connections="OFF",
log_disconnections="OFF",
connection_throttling="OFF",
log_retention_days=None,
firewall=None,
location="location",
)
]
}
@@ -91,14 +93,16 @@ class Test_postgresql_flexible_server_log_retention_days_greater_3:
id=postgresql_server_id,
name=postgresql_server_name,
resource_group="resource_group",
location="location",
require_secure_transport="OFF",
active_directory_auth=None,
entra_id_admins=[],
log_checkpoints="OFF",
log_connections="OFF",
log_disconnections="OFF",
connection_throttling="OFF",
log_retention_days=log_retention_days,
firewall=None,
location="location",
)
]
}
@@ -141,14 +145,16 @@ class Test_postgresql_flexible_server_log_retention_days_greater_3:
id=postgresql_server_id,
name=postgresql_server_name,
resource_group="resource_group",
location="location",
require_secure_transport="OFF",
active_directory_auth=None,
entra_id_admins=[],
log_checkpoints="OFF",
log_connections="OFF",
log_disconnections="OFF",
connection_throttling="OFF",
log_retention_days=log_retention_days,
firewall=None,
location="location",
)
]
}
@@ -191,14 +197,16 @@ class Test_postgresql_flexible_server_log_retention_days_greater_3:
id=postgresql_server_id,
name=postgresql_server_name,
resource_group="resource_group",
location="location",
require_secure_transport="OFF",
active_directory_auth=None,
entra_id_admins=[],
log_checkpoints="OFF",
log_connections="OFF",
log_disconnections="OFF",
connection_throttling="OFF",
log_retention_days=log_retention_days,
firewall=None,
location="location",
)
]
}

View File

@@ -1,6 +1,7 @@
from unittest.mock import patch
from prowler.providers.azure.services.postgresql.postgresql_service import (
EntraIdAdmin,
Firewall,
PostgreSQL,
Server,
@@ -24,14 +25,23 @@ def mock_sqlserver_get_postgresql_flexible_servers(_):
id="id",
name="name",
resource_group="resource_group",
location="location",
require_secure_transport="ON",
active_directory_auth="ENABLED",
entra_id_admins=[
EntraIdAdmin(
object_id="11111111-1111-1111-1111-111111111111",
principal_name="Test Admin User",
principal_type="User",
tenant_id="22222222-2222-2222-2222-222222222222",
)
],
log_checkpoints="ON",
log_connections="ON",
log_disconnections="ON",
connection_throttling="ON",
log_retention_days="3",
firewall=[firewall],
location="location",
)
]
}
@@ -112,6 +122,22 @@ class Test_SqlServer_Service:
== "3"
)
def test_get_active_directory_auth(self):
postgresql = PostgreSQL(set_mocked_azure_provider())
assert (
postgresql.flexible_servers[AZURE_SUBSCRIPTION_ID][0].active_directory_auth
== "ENABLED"
)
def test_get_entra_id_admins(self):
postgresql = PostgreSQL(set_mocked_azure_provider())
admins = postgresql.flexible_servers[AZURE_SUBSCRIPTION_ID][0].entra_id_admins
assert isinstance(admins, list)
assert len(admins) == 1
assert isinstance(admins[0], EntraIdAdmin)
assert admins[0].principal_name == "Test Admin User"
assert admins[0].object_id == "11111111-1111-1111-1111-111111111111"
def test_get_firewall(self):
postgesql = PostgreSQL(set_mocked_azure_provider())
assert (