mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-04 02:04:06 +00:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dbab48ea97 | ||
|
|
a74ff0f8f4 |
@@ -6,6 +6,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
|
||||
|
||||
### 🚀 Added
|
||||
|
||||
- `gmail_dkim_enabled_all_domains` check for googleworkspace provider [(#11381)](https://github.com/prowler-cloud/prowler/pull/11381)
|
||||
- `application` service for Okta provider with `application_admin_console_session_idle_timeout_15min`, `application_admin_console_mfa_required`, `application_admin_console_phishing_resistant_authentication`, `application_dashboard_mfa_required`, `application_dashboard_phishing_resistant_authentication`, and `application_authentication_policy_network_zone_enforced` checks [(#11358)](https://github.com/prowler-cloud/prowler/pull/11358)
|
||||
- AWS AI Security Framework compliance for AWS provider [(#11353)](https://github.com/prowler-cloud/prowler/pull/11353)
|
||||
- `storage_account_public_network_access_disabled` check for Azure provider and remapped the Azure CIS "Public Network Access is Disabled" requirements to it [(#11334)](https://github.com/prowler-cloud/prowler/pull/11334)
|
||||
|
||||
@@ -569,7 +569,9 @@
|
||||
{
|
||||
"Id": "3.1.3.2.1",
|
||||
"Description": "Ensure that DKIM is enabled for all mail enabled domains",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"gmail_dkim_enabled_all_domains"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "3 Apps",
|
||||
|
||||
@@ -600,7 +600,9 @@
|
||||
{
|
||||
"Id": "GWS.GMAIL.2.1",
|
||||
"Description": "DKIM SHOULD be enabled for all domains",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"gmail_dkim_enabled_all_domains"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "Gmail",
|
||||
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"Provider": "googleworkspace",
|
||||
"CheckID": "gmail_dkim_enabled_all_domains",
|
||||
"CheckTitle": "DKIM ensures email authenticity and integrity for all mail-enabled domains",
|
||||
"CheckType": [],
|
||||
"ServiceName": "gmail",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "high",
|
||||
"ResourceType": "NotDefined",
|
||||
"ResourceGroup": "collaboration",
|
||||
"Description": "DKIM (DomainKeys Identified Mail) adds a **cryptographic signature** to outgoing email headers, allowing receiving servers to verify that messages originate from the domain and were not altered in transit.\n\nThis check evaluates whether every mail-enabled domain in the Google Workspace tenant has a DKIM signing key generated and authentication started.",
|
||||
"Risk": "Without DKIM, attackers can more easily **spoof the organization's domains**, increasing the risk of **phishing, business email compromise, and reduced mail deliverability**. Receiving servers that require DKIM may reject or flag legitimate messages, harming trusted communication.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/174124",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "1. Sign in to the Google **Admin console** at https://admin.google.com\n2. Navigate to **Apps** > **Google Workspace** > **Gmail**\n3. Click **Authenticate email**\n4. Select the domain to configure\n5. Click **Generate new record** (select 2048-bit key length if your DNS host supports it)\n6. Add the generated TXT record to your domain's DNS configuration at the selector shown (default: `google._domainkey`)\n7. Wait for DNS propagation, then click **Start authentication**\n8. Repeat for every mail-enabled domain and secondary domain",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Generate and activate a DKIM signing key for every mail-enabled domain. Use a **2048-bit key** where the DNS host supports it to ensure stronger cryptographic protection against message forgery.",
|
||||
"Url": "https://hub.prowler.com/check/gmail_dkim_enabled_all_domains"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"email-security"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [
|
||||
"gmail_domain_spoofing_protection_enabled",
|
||||
"gmail_inbound_domain_spoofing_protection_enabled",
|
||||
"gmail_unauthenticated_email_protection_enabled"
|
||||
],
|
||||
"Notes": "This check is Manual because no public Google Admin SDK/API endpoint exposes the Gmail DKIM authentication status. Verification requires inspecting the Admin Console and performing a DNS TXT record lookup (e.g., dig TXT google._domainkey.<domain>)."
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
from typing import List
|
||||
|
||||
from prowler.lib.check.models import Check, CheckReportGoogleWorkspace
|
||||
from prowler.providers.googleworkspace.services.gmail.gmail_client import gmail_client
|
||||
|
||||
|
||||
class gmail_dkim_enabled_all_domains(Check):
|
||||
"""Verify that DKIM is enabled for all mail-enabled domains.
|
||||
|
||||
DKIM (DomainKeys Identified Mail) adds a cryptographic signature to
|
||||
outgoing messages, allowing receivers to verify authenticity and integrity.
|
||||
Because there is no public Admin SDK/API endpoint to query DKIM status,
|
||||
this check always returns MANUAL and directs the administrator to verify
|
||||
DKIM configuration in the Google Admin Console and via DNS lookup.
|
||||
|
||||
- MANUAL: DKIM authentication status must be verified manually in the
|
||||
Admin Console and through DNS TXT record inspection for each domain.
|
||||
"""
|
||||
|
||||
def execute(self) -> List[CheckReportGoogleWorkspace]:
|
||||
"""Execute the DKIM enabled check.
|
||||
|
||||
Returns:
|
||||
A list of reports with MANUAL status requiring administrator
|
||||
verification of DKIM configuration per domain.
|
||||
"""
|
||||
findings = []
|
||||
|
||||
report = CheckReportGoogleWorkspace(
|
||||
metadata=self.metadata(),
|
||||
resource=gmail_client.policies,
|
||||
resource_id="gmailPolicies",
|
||||
resource_name="Gmail Policies",
|
||||
customer_id=gmail_client.provider.identity.customer_id,
|
||||
)
|
||||
|
||||
report.status = "MANUAL"
|
||||
report.status_extended = (
|
||||
f"DKIM authentication status for domain "
|
||||
f"{gmail_client.provider.identity.domain} cannot be automatically "
|
||||
f"verified because no public Admin SDK/API endpoint exposes this "
|
||||
f"setting. Verify in the Admin Console under Apps > Google "
|
||||
f"Workspace > Gmail > Authenticate email that DKIM signing is "
|
||||
f"generated and authentication is started for every mail-enabled "
|
||||
f"domain, and confirm via DNS that a valid TXT record exists at "
|
||||
f"google._domainkey.{gmail_client.provider.identity.domain}."
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
from prowler.providers.googleworkspace.models import GoogleWorkspaceIdentityInfo
|
||||
from prowler.providers.googleworkspace.services.gmail.gmail_service import GmailPolicies
|
||||
from tests.providers.googleworkspace.googleworkspace_fixtures import (
|
||||
CUSTOMER_ID,
|
||||
DELEGATED_USER,
|
||||
DOMAIN,
|
||||
ROOT_ORG_UNIT_ID,
|
||||
set_mocked_googleworkspace_provider,
|
||||
)
|
||||
|
||||
|
||||
class TestGmailDkimEnabledAllDomains:
|
||||
"""Tests for the gmail_dkim_enabled_all_domains check.
|
||||
|
||||
Since DKIM status is not exposed through any public Admin SDK/API,
|
||||
this check always returns MANUAL to prompt administrator verification.
|
||||
"""
|
||||
|
||||
def test_manual_status(self):
|
||||
"""Check always returns MANUAL because DKIM status cannot be queried via API."""
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.gmail.gmail_dkim_enabled_all_domains.gmail_dkim_enabled_all_domains.gmail_client"
|
||||
) as mock_client,
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.gmail.gmail_dkim_enabled_all_domains.gmail_dkim_enabled_all_domains import (
|
||||
gmail_dkim_enabled_all_domains,
|
||||
)
|
||||
|
||||
mock_client.provider = mock_provider
|
||||
mock_client.policies = GmailPolicies()
|
||||
|
||||
check = gmail_dkim_enabled_all_domains()
|
||||
findings = check.execute()
|
||||
|
||||
assert len(findings) == 1
|
||||
assert findings[0].status == "MANUAL"
|
||||
assert findings[0].resource_name == "Gmail Policies"
|
||||
assert findings[0].resource_id == "gmailPolicies"
|
||||
assert findings[0].customer_id == CUSTOMER_ID
|
||||
assert findings[0].resource == GmailPolicies().dict()
|
||||
|
||||
def test_manual_status_extended_contains_domain(self):
|
||||
"""Verify the status_extended message references the tenant domain."""
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.gmail.gmail_dkim_enabled_all_domains.gmail_dkim_enabled_all_domains.gmail_client"
|
||||
) as mock_client,
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.gmail.gmail_dkim_enabled_all_domains.gmail_dkim_enabled_all_domains import (
|
||||
gmail_dkim_enabled_all_domains,
|
||||
)
|
||||
|
||||
mock_client.provider = mock_provider
|
||||
mock_client.policies = GmailPolicies()
|
||||
|
||||
check = gmail_dkim_enabled_all_domains()
|
||||
findings = check.execute()
|
||||
|
||||
assert len(findings) == 1
|
||||
assert DOMAIN in findings[0].status_extended
|
||||
assert "cannot be automatically verified" in findings[0].status_extended
|
||||
assert f"google._domainkey.{DOMAIN}" in findings[0].status_extended
|
||||
|
||||
def test_manual_status_extended_contains_admin_console_guidance(self):
|
||||
"""Verify the status_extended message includes Admin Console verification instructions."""
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.gmail.gmail_dkim_enabled_all_domains.gmail_dkim_enabled_all_domains.gmail_client"
|
||||
) as mock_client,
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.gmail.gmail_dkim_enabled_all_domains.gmail_dkim_enabled_all_domains import (
|
||||
gmail_dkim_enabled_all_domains,
|
||||
)
|
||||
|
||||
mock_client.provider = mock_provider
|
||||
mock_client.policies = GmailPolicies()
|
||||
|
||||
check = gmail_dkim_enabled_all_domains()
|
||||
findings = check.execute()
|
||||
|
||||
assert len(findings) == 1
|
||||
assert "Admin Console" in findings[0].status_extended
|
||||
assert "Authenticate email" in findings[0].status_extended
|
||||
assert "DKIM" in findings[0].status_extended
|
||||
|
||||
def test_manual_status_with_custom_domain(self):
|
||||
"""Verify the check correctly references a custom domain in the output."""
|
||||
custom_domain = "custom-org.io"
|
||||
custom_customer_id = "C9876543"
|
||||
mock_provider = set_mocked_googleworkspace_provider(
|
||||
identity=GoogleWorkspaceIdentityInfo(
|
||||
domain=custom_domain,
|
||||
customer_id=custom_customer_id,
|
||||
delegated_user=f"admin@{custom_domain}",
|
||||
root_org_unit_id=ROOT_ORG_UNIT_ID,
|
||||
profile="default",
|
||||
),
|
||||
)
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.gmail.gmail_dkim_enabled_all_domains.gmail_dkim_enabled_all_domains.gmail_client"
|
||||
) as mock_client,
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.gmail.gmail_dkim_enabled_all_domains.gmail_dkim_enabled_all_domains import (
|
||||
gmail_dkim_enabled_all_domains,
|
||||
)
|
||||
|
||||
mock_client.provider = mock_provider
|
||||
mock_client.policies = GmailPolicies()
|
||||
|
||||
check = gmail_dkim_enabled_all_domains()
|
||||
findings = check.execute()
|
||||
|
||||
assert len(findings) == 1
|
||||
assert findings[0].status == "MANUAL"
|
||||
assert findings[0].customer_id == custom_customer_id
|
||||
assert custom_domain in findings[0].status_extended
|
||||
assert f"google._domainkey.{custom_domain}" in findings[0].status_extended
|
||||
# Ensure default domain is NOT referenced
|
||||
assert DOMAIN not in findings[0].status_extended
|
||||
|
||||
def test_single_finding_returned(self):
|
||||
"""Check always produces exactly one finding (one MANUAL report per execution)."""
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.gmail.gmail_dkim_enabled_all_domains.gmail_dkim_enabled_all_domains.gmail_client"
|
||||
) as mock_client,
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.gmail.gmail_dkim_enabled_all_domains.gmail_dkim_enabled_all_domains import (
|
||||
gmail_dkim_enabled_all_domains,
|
||||
)
|
||||
|
||||
mock_client.provider = mock_provider
|
||||
mock_client.policies = GmailPolicies()
|
||||
|
||||
check = gmail_dkim_enabled_all_domains()
|
||||
findings = check.execute()
|
||||
|
||||
assert len(findings) == 1
|
||||
assert findings[0].status != "PASS"
|
||||
assert findings[0].status != "FAIL"
|
||||
assert findings[0].status == "MANUAL"
|
||||
Reference in New Issue
Block a user