chore(aws): support new eusc partition (#9649)

Co-authored-by: Pepe Fagoaga <pepe@prowler.com>
This commit is contained in:
Pedro Martín
2025-12-23 12:28:10 +01:00
committed by GitHub
parent 6f018183cd
commit 8d1d041092
13 changed files with 777 additions and 139 deletions

View File

@@ -220,6 +220,7 @@ The function returns a JSON file containing the list of regions for the provider
"sa-east-1", "us-east-1", "us-east-2", "us-west-1", "us-west-2" "sa-east-1", "us-east-1", "us-east-2", "us-west-1", "us-west-2"
], ],
"aws-cn": ["cn-north-1", "cn-northwest-1"], "aws-cn": ["cn-north-1", "cn-northwest-1"],
"aws-eusc": ["eusc-de-east-1"],
"aws-us-gov": ["us-gov-east-1", "us-gov-west-1"] "aws-us-gov": ["us-gov-east-1", "us-gov-west-1"]
} }
} }

View File

@@ -6,15 +6,16 @@ By default Prowler is able to scan the following AWS partitions:
- Commercial: `aws` - Commercial: `aws`
- China: `aws-cn` - China: `aws-cn`
- European Sovereign Cloud: `aws-eusc`
- GovCloud (US): `aws-us-gov` - GovCloud (US): `aws-us-gov`
<Note> <Note>
To check the available regions for each partition and service, refer to: [aws\_regions\_by\_service.json](https://github.com/prowler-cloud/prowler/blob/master/prowler/providers/aws/aws_regions_by_service.json) To check the available regions for each partition and service, refer to: [aws\_regions\_by\_service.json](https://github.com/prowler-cloud/prowler/blob/master/prowler/providers/aws/aws_regions_by_service.json)
</Note> </Note>
## Scanning AWS China and GovCloud Partitions in Prowler ## Scanning AWS China, European Sovereign Cloud and GovCloud Partitions in Prowler
When scanning the China (`aws-cn`) or GovCloud (`aws-us-gov`), ensure one of the following: When scanning the China (`aws-cn`), European Sovereign Cloud (`aws-eusc`) or GovCloud (`aws-us-gov`) partitions, ensure one of the following:
- Your AWS credentials include a valid region within the desired partition. - Your AWS credentials include a valid region within the desired partition.
@@ -83,6 +84,29 @@ To scan an account in the AWS GovCloud (US) partition (`aws-us-gov`):
<Note> <Note>
With this configuration, all partition regions will be scanned without needing the `-f/--region` flag With this configuration, all partition regions will be scanned without needing the `-f/--region` flag
</Note>
### AWS European Sovereign Cloud
To scan an account in the AWS European Sovereign Cloud partition (`aws-eusc`):
- By using the `-f/--region` flag:
```
prowler aws --region eusc-de-east-1
```
- By using the region configured in your AWS profile at `~/.aws/credentials` or `~/.aws/config`:
```
[default]
aws_access_key_id = XXXXXXXXXXXXXXXXXXX
aws_secret_access_key = XXXXXXXXXXXXXXXXXXX
region = eusc-de-east-1
```
<Note>
With this configuration, all partition regions will be scanned without needing the `-f/--region` flag
</Note> </Note>
### AWS ISO (US \& Europe) ### AWS ISO (US \& Europe)
@@ -99,6 +123,9 @@ The AWS ISO partitions—commonly referred to as "secret partitions"—are air-g
"cn-north-1", "cn-north-1",
"cn-northwest-1" "cn-northwest-1"
], ],
"aws-eusc": [
"eusc-de-east-1"
],
"aws-us-gov": [ "aws-us-gov": [
"us-gov-east-1", "us-gov-east-1",
"us-gov-west-1" "us-gov-west-1"

View File

@@ -6,14 +6,13 @@ across all providers.
from typing import Any from typing import Any
from pydantic import Field
from prowler_mcp_server.prowler_app.models.resources import ( from prowler_mcp_server.prowler_app.models.resources import (
DetailedResource, DetailedResource,
ResourcesListResponse, ResourcesListResponse,
ResourcesMetadataResponse, ResourcesMetadataResponse,
) )
from prowler_mcp_server.prowler_app.tools.base import BaseTool from prowler_mcp_server.prowler_app.tools.base import BaseTool
from pydantic import Field
class ResourcesTools(BaseTool): class ResourcesTools(BaseTool):
@@ -188,7 +187,7 @@ class ResourcesTools(BaseTool):
1. Configuration Details: 1. Configuration Details:
- metadata: Provider-specific configuration (tags, policies, encryption settings, network rules) - metadata: Provider-specific configuration (tags, policies, encryption settings, network rules)
- partition: Provider-specific partition/region grouping (e.g., aws, aws-cn, aws-us-gov for AWS) - partition: Provider-specific partition/region grouping (e.g., aws, aws-cn, aws-eusc, aws-us-gov for AWS)
2. Temporal Tracking: 2. Temporal Tracking:
- inserted_at: When Prowler first discovered this resource - inserted_at: When Prowler first discovered this resource

View File

@@ -7,6 +7,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
### Added ### Added
- Add Prowler ThreatScore for the Alibaba Cloud provider [(#9511)](https://github.com/prowler-cloud/prowler/pull/9511) - Add Prowler ThreatScore for the Alibaba Cloud provider [(#9511)](https://github.com/prowler-cloud/prowler/pull/9511)
- `compute_instance_group_multiple_zones` check for GCP provider [(#9566)](https://github.com/prowler-cloud/prowler/pull/9566) - `compute_instance_group_multiple_zones` check for GCP provider [(#9566)](https://github.com/prowler-cloud/prowler/pull/9566)
- Support AWS European Sovereign Cloud [(#9649)](https://github.com/prowler-cloud/prowler/pull/9649)
- `compute_instance_disk_auto_delete_disabled` check for GCP provider [(#9604)](https://github.com/prowler-cloud/prowler/pull/9604) - `compute_instance_disk_auto_delete_disabled` check for GCP provider [(#9604)](https://github.com/prowler-cloud/prowler/pull/9604)
- Bedrock service pagination [(#9606)](https://github.com/prowler-cloud/prowler/pull/9606) - Bedrock service pagination [(#9606)](https://github.com/prowler-cloud/prowler/pull/9606)

View File

@@ -984,6 +984,8 @@ class AwsProvider(Provider):
global_region = "us-east-1" global_region = "us-east-1"
if self._identity.partition == "aws-cn": if self._identity.partition == "aws-cn":
global_region = "cn-north-1" global_region = "cn-north-1"
elif self._identity.partition == "aws-eusc":
global_region = "eusc-de-east-1"
elif self._identity.partition == "aws-us-gov": elif self._identity.partition == "aws-us-gov":
global_region = "us-gov-east-1" global_region = "us-gov-east-1"
elif "aws-iso" in self._identity.partition: elif "aws-iso" in self._identity.partition:
@@ -1473,11 +1475,12 @@ class AwsProvider(Provider):
sts_client = create_sts_session(session, 'us-west-2') sts_client = create_sts_session(session, 'us-west-2')
""" """
try: try:
sts_endpoint_url = ( if aws_region.startswith("cn-"):
f"https://sts.{aws_region}.amazonaws.com" sts_endpoint_url = f"https://sts.{aws_region}.amazonaws.com.cn"
if not aws_region.startswith("cn-") elif aws_region.startswith("eusc-"):
else f"https://sts.{aws_region}.amazonaws.com.cn" sts_endpoint_url = f"https://sts.{aws_region}.amazonaws.eu"
) else:
sts_endpoint_url = f"https://sts.{aws_region}.amazonaws.com"
return session.client("sts", aws_region, endpoint_url=sts_endpoint_url) return session.client("sts", aws_region, endpoint_url=sts_endpoint_url)
except Exception as error: except Exception as error:
logger.critical( logger.critical(

File diff suppressed because it is too large Load Diff

View File

@@ -59,5 +59,5 @@ def parse_iam_credentials_arn(arn: str) -> ARN:
def is_valid_arn(arn: str) -> bool: def is_valid_arn(arn: str) -> bool:
"""is_valid_arn returns True or False whether the given AWS ARN (Amazon Resource Name) is valid or not.""" """is_valid_arn returns True or False whether the given AWS ARN (Amazon Resource Name) is valid or not."""
regex = r"^arn:aws(-cn|-us-gov|-iso|-iso-b)?:[a-zA-Z0-9\-]+:([a-z]{2}-[a-z]+-\d{1})?:(\d{12})?:[a-zA-Z0-9\-_\/:\.\*]+(:\d+)?$" regex = r"^arn:aws(-cn|-eusc|-us-gov|-iso|-iso-b)?:[a-zA-Z0-9\-]+:([a-z]{2}-[a-z]+-\d{1})?:(\d{12})?:[a-zA-Z0-9\-_\/:\.\*]+(:\d+)?$"
return re.match(regex, arn) is not None return re.match(regex, arn) is not None

View File

@@ -55,7 +55,7 @@ class SecurityHubConnection(Connection):
Attributes: Attributes:
enabled_regions (set): Set of regions where Security Hub is enabled. enabled_regions (set): Set of regions where Security Hub is enabled.
disabled_regions (set): Set of regions where Security Hub is disabled. disabled_regions (set): Set of regions where Security Hub is disabled.
partition (str): AWS partition (e.g., aws, aws-cn, aws-us-gov) where SecurityHub is deployed. partition (str): AWS partition (e.g., aws, aws-cn, aws-eusc, aws-us-gov) where SecurityHub is deployed.
""" """
enabled_regions: set = None enabled_regions: set = None
@@ -70,7 +70,7 @@ class SecurityHub:
Attributes: Attributes:
_session (Session): AWS session object for authentication and communication with AWS services. _session (Session): AWS session object for authentication and communication with AWS services.
_aws_account_id (str): AWS account ID associated with the SecurityHub instance. _aws_account_id (str): AWS account ID associated with the SecurityHub instance.
_aws_partition (str): AWS partition (e.g., aws, aws-cn, aws-us-gov) where SecurityHub is deployed. _aws_partition (str): AWS partition (e.g., aws, aws-cn, aws-eusc, aws-us-gov) where SecurityHub is deployed.
_findings_per_region (dict): Dictionary containing findings per region. _findings_per_region (dict): Dictionary containing findings per region.
_enabled_regions (dict): Dictionary containing enabled regions with SecurityHub clients. _enabled_regions (dict): Dictionary containing enabled regions with SecurityHub clients.
@@ -115,7 +115,7 @@ class SecurityHub:
Args: Args:
- aws_session (Session): AWS session object for authentication and communication with AWS services. - aws_session (Session): AWS session object for authentication and communication with AWS services.
- aws_account_id (str): AWS account ID associated with the SecurityHub instance. - aws_account_id (str): AWS account ID associated with the SecurityHub instance.
- aws_partition (str): AWS partition (e.g., aws, aws-cn, aws-us-gov) where SecurityHub is deployed. - aws_partition (str): AWS partition (e.g., aws, aws-cn, aws-eusc, aws-us-gov) where SecurityHub is deployed.
- findings (list[AWSSecurityFindingFormat]): List of findings to filter and send to Security Hub. - findings (list[AWSSecurityFindingFormat]): List of findings to filter and send to Security Hub.
- aws_security_hub_available_regions (list[str]): List of regions where Security Hub is available. - aws_security_hub_available_regions (list[str]): List of regions where Security Hub is available.
- send_only_fails (bool): Flag indicating whether to send only findings with status 'FAIL'. - send_only_fails (bool): Flag indicating whether to send only findings with status 'FAIL'.
@@ -477,7 +477,7 @@ class SecurityHub:
Args: Args:
aws_account_id (str): AWS account ID to check for Prowler integration. aws_account_id (str): AWS account ID to check for Prowler integration.
aws_partition (str): AWS partition (e.g., aws, aws-cn, aws-us-gov). aws_partition (str): AWS partition (e.g., aws, aws-cn, aws-eusc, aws-us-gov).
regions (set): Set of regions to check for Security Hub integration. regions (set): Set of regions to check for Security Hub integration.
raise_on_exception (bool): Whether to raise an exception if an error occurs. raise_on_exception (bool): Whether to raise an exception if an error occurs.
profile (str): AWS profile name to use for authentication. profile (str): AWS profile name to use for authentication.

View File

@@ -90,6 +90,7 @@ class Partition(str, Enum):
Attributes: Attributes:
aws (str): Represents the standard AWS commercial regions. aws (str): Represents the standard AWS commercial regions.
aws_cn (str): Represents the AWS China regions. aws_cn (str): Represents the AWS China regions.
aws_eusc (str): Represents the AWS European Sovereign Cloud regions.
aws_us_gov (str): Represents the AWS GovCloud (US) Regions. aws_us_gov (str): Represents the AWS GovCloud (US) Regions.
aws_iso (str): Represents the AWS ISO (US) Regions. aws_iso (str): Represents the AWS ISO (US) Regions.
aws_iso_b (str): Represents the AWS ISOB (US) Regions. aws_iso_b (str): Represents the AWS ISOB (US) Regions.
@@ -99,6 +100,7 @@ class Partition(str, Enum):
aws = "aws" aws = "aws"
aws_cn = "aws-cn" aws_cn = "aws-cn"
aws_eusc = "aws-eusc"
aws_us_gov = "aws-us-gov" aws_us_gov = "aws-us-gov"
aws_iso = "aws-iso" aws_iso = "aws-iso"
aws_iso_b = "aws-iso-b" aws_iso_b = "aws-iso-b"

View File

@@ -45,6 +45,7 @@ from tests.providers.aws.utils import (
AWS_ACCOUNT_NUMBER, AWS_ACCOUNT_NUMBER,
AWS_CHINA_PARTITION, AWS_CHINA_PARTITION,
AWS_COMMERCIAL_PARTITION, AWS_COMMERCIAL_PARTITION,
AWS_EUSC_PARTITION,
AWS_GOV_CLOUD_ACCOUNT_ARN, AWS_GOV_CLOUD_ACCOUNT_ARN,
AWS_GOV_CLOUD_PARTITION, AWS_GOV_CLOUD_PARTITION,
AWS_ISO_PARTITION, AWS_ISO_PARTITION,
@@ -52,6 +53,7 @@ from tests.providers.aws.utils import (
AWS_REGION_CN_NORTHWEST_1, AWS_REGION_CN_NORTHWEST_1,
AWS_REGION_EU_CENTRAL_1, AWS_REGION_EU_CENTRAL_1,
AWS_REGION_EU_WEST_1, AWS_REGION_EU_WEST_1,
AWS_REGION_EUSC_DE_EAST_1,
AWS_REGION_GOV_CLOUD_US_EAST_1, AWS_REGION_GOV_CLOUD_US_EAST_1,
AWS_REGION_ISO_GLOBAL, AWS_REGION_ISO_GLOBAL,
AWS_REGION_US_EAST_1, AWS_REGION_US_EAST_1,
@@ -956,6 +958,13 @@ aws:
assert aws_provider.get_global_region() == AWS_REGION_ISO_GLOBAL assert aws_provider.get_global_region() == AWS_REGION_ISO_GLOBAL
@mock_aws
def test_aws_eusc_get_global_region(self):
aws_provider = AwsProvider()
aws_provider._identity.partition = AWS_EUSC_PARTITION
assert aws_provider.get_global_region() == AWS_REGION_EUSC_DE_EAST_1
@mock_aws @mock_aws
def test_get_available_aws_service_regions_with_us_east_1_audited(self): def test_get_available_aws_service_regions_with_us_east_1_audited(self):
region = [AWS_REGION_US_EAST_1] region = [AWS_REGION_US_EAST_1]
@@ -1506,6 +1515,17 @@ aws:
sts_session._endpoint.host == f"https://sts.{aws_region}.amazonaws.com.cn" sts_session._endpoint.host == f"https://sts.{aws_region}.amazonaws.com.cn"
) )
@mock_aws
def test_create_sts_session_eusc(self):
current_session = session.Session()
aws_region = AWS_REGION_EUSC_DE_EAST_1
sts_session = AwsProvider.create_sts_session(current_session, aws_region)
assert sts_session._service_model.service_name == "sts"
assert sts_session._client_config.region_name == aws_region
assert sts_session._endpoint._endpoint_prefix == "sts"
assert sts_session._endpoint.host == f"https://sts.{aws_region}.amazonaws.eu"
@mock_aws @mock_aws
@patch( @patch(
"prowler.lib.check.utils.recover_checks_from_provider", "prowler.lib.check.utils.recover_checks_from_provider",
@@ -1760,7 +1780,7 @@ aws:
assert len(AwsProvider.get_regions("aws-cn")) == 2 assert len(AwsProvider.get_regions("aws-cn")) == 2
def test_get_regions_aws_count(self): def test_get_regions_aws_count(self):
assert len(AwsProvider.get_regions(partition="aws")) == 35 assert len(AwsProvider.get_regions(partition="aws")) == 34
def test_get_all_regions(self): def test_get_all_regions(self):
with patch( with patch(

View File

@@ -19,6 +19,7 @@ IAM_ROLE = "test-role"
IAM_SERVICE = "iam" IAM_SERVICE = "iam"
COMMERCIAL_PARTITION = "aws" COMMERCIAL_PARTITION = "aws"
CHINA_PARTITION = "aws-cn" CHINA_PARTITION = "aws-cn"
EUSC_PARTITION = "aws-eusc"
GOVCLOUD_PARTITION = "aws-us-gov" GOVCLOUD_PARTITION = "aws-us-gov"
@@ -245,6 +246,28 @@ class Test_ARN_Parsing:
"resource": IAM_ROLE, "resource": IAM_ROLE,
}, },
}, },
{
"input_arn": f"arn:{EUSC_PARTITION}:{IAM_SERVICE}::{ACCOUNT_ID}:{RESOURCE_TYPE_ROLE}/{IAM_ROLE}",
"expected": {
"partition": EUSC_PARTITION,
"service": IAM_SERVICE,
"region": None,
"account_id": ACCOUNT_ID,
"resource_type": RESOURCE_TYPE_ROLE,
"resource": IAM_ROLE,
},
},
{
"input_arn": f"arn:{EUSC_PARTITION}:{IAM_SERVICE}::{ACCOUNT_ID}:{RESOUCE_TYPE_USER}/{IAM_ROLE}",
"expected": {
"partition": EUSC_PARTITION,
"service": IAM_SERVICE,
"region": None,
"account_id": ACCOUNT_ID,
"resource_type": RESOUCE_TYPE_USER,
"resource": IAM_ROLE,
},
},
# Root user # Root user
{ {
"input_arn": f"arn:aws:{IAM_SERVICE}::{ACCOUNT_ID}:root", "input_arn": f"arn:aws:{IAM_SERVICE}::{ACCOUNT_ID}:root",
@@ -279,6 +302,17 @@ class Test_ARN_Parsing:
"resource": "root", "resource": "root",
}, },
}, },
{
"input_arn": f"arn:{EUSC_PARTITION}:{IAM_SERVICE}::{ACCOUNT_ID}:root",
"expected": {
"partition": EUSC_PARTITION,
"service": IAM_SERVICE,
"region": None,
"account_id": ACCOUNT_ID,
"resource_type": "root",
"resource": "root",
},
},
{ {
"input_arn": f"arn:aws:sts::{ACCOUNT_ID}:federated-user/Bob", "input_arn": f"arn:aws:sts::{ACCOUNT_ID}:federated-user/Bob",
"expected": { "expected": {
@@ -312,6 +346,17 @@ class Test_ARN_Parsing:
"resource": "Bob", "resource": "Bob",
}, },
}, },
{
"input_arn": f"arn:{EUSC_PARTITION}:sts::{ACCOUNT_ID}:federated-user/Bob",
"expected": {
"partition": EUSC_PARTITION,
"service": "sts",
"region": None,
"account_id": ACCOUNT_ID,
"resource_type": "federated-user",
"resource": "Bob",
},
},
] ]
for test in test_cases: for test in test_cases:
input_arn = test["input_arn"] input_arn = test["input_arn"]
@@ -379,6 +424,7 @@ class Test_ARN_Parsing:
def test_is_valid_arn(self): def test_is_valid_arn(self):
assert is_valid_arn("arn:aws:iam::012345678910:user/test") assert is_valid_arn("arn:aws:iam::012345678910:user/test")
assert is_valid_arn("arn:aws-cn:ec2:us-east-1:123456789012:vpc/vpc-12345678") assert is_valid_arn("arn:aws-cn:ec2:us-east-1:123456789012:vpc/vpc-12345678")
assert is_valid_arn("arn:aws-eusc:ec2:us-east-1:123456789012:vpc/vpc-12345678")
assert is_valid_arn("arn:aws-us-gov:s3:::bucket") assert is_valid_arn("arn:aws-us-gov:s3:::bucket")
assert is_valid_arn("arn:aws-iso:iam::012345678910:user/test") assert is_valid_arn("arn:aws-iso:iam::012345678910:user/test")
assert is_valid_arn("arn:aws-iso-b:ec2:us-east-1:123456789012:vpc/vpc-12345678") assert is_valid_arn("arn:aws-iso-b:ec2:us-east-1:123456789012:vpc/vpc-12345678")

View File

@@ -17,6 +17,7 @@ from prowler.providers.common.models import Audit_Metadata
AWS_COMMERCIAL_PARTITION = "aws" AWS_COMMERCIAL_PARTITION = "aws"
AWS_GOV_CLOUD_PARTITION = "aws-us-gov" AWS_GOV_CLOUD_PARTITION = "aws-us-gov"
AWS_CHINA_PARTITION = "aws-cn" AWS_CHINA_PARTITION = "aws-cn"
AWS_EUSC_PARTITION = "aws-eusc"
AWS_ISO_PARTITION = "aws-iso" AWS_ISO_PARTITION = "aws-iso"
# Root AWS Account # Root AWS Account
@@ -52,6 +53,9 @@ AWS_REGION_GOV_CLOUD_US_EAST_1 = "us-gov-east-1"
# Iso Regions # Iso Regions
AWS_REGION_ISO_GLOBAL = "aws-iso-global" AWS_REGION_ISO_GLOBAL = "aws-iso-global"
# European Sovereign Cloud Regions
AWS_REGION_EUSC_DE_EAST_1 = "eusc-de-east-1"
# EC2 # EC2
EXAMPLE_AMI_ID = "ami-12c6146b" EXAMPLE_AMI_ID = "ami-12c6146b"

View File

@@ -25,7 +25,7 @@ for page in get_parameters_by_path_paginator.paginate(
for service in page["Parameters"]: for service in page["Parameters"]:
regions_by_service["services"][service["Value"]] = {} regions_by_service["services"][service["Value"]] = {}
# Get all AWS Regions for the specific service # Get all AWS Regions for the specific service
regions = {"aws": [], "aws-cn": [], "aws-us-gov": []} regions = {"aws": [], "aws-cn": [], "aws-eusc": [], "aws-us-gov": []}
for page in get_parameters_by_path_paginator.paginate( for page in get_parameters_by_path_paginator.paginate(
Path="/aws/service/global-infrastructure/services/" Path="/aws/service/global-infrastructure/services/"
+ service["Value"] + service["Value"]
@@ -34,6 +34,8 @@ for page in get_parameters_by_path_paginator.paginate(
for region in page["Parameters"]: for region in page["Parameters"]:
if "cn" in region["Value"]: if "cn" in region["Value"]:
regions["aws-cn"].append(region["Value"]) regions["aws-cn"].append(region["Value"])
elif "eusc" in region["Value"]:
regions["aws-eusc"].append(region["Value"])
elif "gov" in region["Value"]: elif "gov" in region["Value"]:
regions["aws-us-gov"].append(region["Value"]) regions["aws-us-gov"].append(region["Value"])
else: else:
@@ -41,6 +43,7 @@ for page in get_parameters_by_path_paginator.paginate(
# Sort regions per partition # Sort regions per partition
regions["aws"] = sorted(regions["aws"]) regions["aws"] = sorted(regions["aws"])
regions["aws-cn"] = sorted(regions["aws-cn"]) regions["aws-cn"] = sorted(regions["aws-cn"])
regions["aws-eusc"] = sorted(regions["aws-eusc"])
regions["aws-us-gov"] = sorted(regions["aws-us-gov"]) regions["aws-us-gov"] = sorted(regions["aws-us-gov"])
regions_by_service["services"][service["Value"]]["regions"] = regions regions_by_service["services"][service["Value"]]["regions"] = regions
@@ -69,6 +72,7 @@ regions_by_service["services"]["bedrock-agent"] = {
"us-west-2", "us-west-2",
], ],
"aws-cn": [], "aws-cn": [],
"aws-eusc": [],
"aws-us-gov": [ "aws-us-gov": [
"us-gov-west-1", "us-gov-west-1",
], ],