From d9dc6c0a4953d3a778f66b580339155d0888765c Mon Sep 17 00:00:00 2001 From: Sergio Garcia <38561120+sergargar@users.noreply.github.com> Date: Fri, 23 Dec 2022 12:32:31 +0100 Subject: [PATCH] fix(global_services): handle global regions correctly (#1594) Co-authored-by: sergargar Co-authored-by: Pepe Fagoaga --- prowler/providers/aws/aws_provider.py | 85 ++- .../providers/aws/aws_regions_by_service.json | 483 +++++++++++++++++- .../aws/services/account/account_service.py | 3 +- .../services/cloudfront/cloudfront_service.py | 17 +- .../services/cloudtrail/cloudtrail_service.py | 7 +- .../globalaccelerator_service.py | 13 +- .../providers/aws/services/iam/iam_service.py | 8 +- .../aws/services/route53/route53_service.py | 27 +- .../providers/aws/services/s3/s3_service.py | 15 +- .../aws/services/shield/check_extra7170 | 48 -- .../aws/services/shield/shield_service.py | 16 +- .../trustedadvisor_errors_and_warnings.py | 29 +- .../trustedadvisor/trustedadvisor_service.py | 53 +- tests/providers/aws/aws_provider_test.py | 91 +++- .../iam_administrator_access_with_mfa_test.py | 10 + .../iam_avoid_root_usage_test.py | 8 + .../iam_check_saml_providers_sts_test.py | 2 + .../iam_disable_30_days_credentials_test.py | 6 + .../iam_disable_45_days_credentials_test.py | 6 + .../iam_disable_90_days_credentials_test.py | 6 + ..._policy_permissive_role_assumption_test.py | 10 + .../aws/services/iam/iam_service_test.py | 6 +- ...trustedadvisor_errors_and_warnings_test.py | 10 +- .../trustedadvisor_service_test.py | 15 +- util/update_aws_services_regions.py | 38 +- 25 files changed, 789 insertions(+), 223 deletions(-) delete mode 100644 prowler/providers/aws/services/shield/check_extra7170 diff --git a/prowler/providers/aws/aws_provider.py b/prowler/providers/aws/aws_provider.py index 6bdb2ff4..34cea459 100644 --- a/prowler/providers/aws/aws_provider.py +++ b/prowler/providers/aws/aws_provider.py @@ -103,58 +103,39 @@ def assume_role(audit_info: AWS_Audit_Info) -> dict: return assumed_credentials -def generate_regional_clients(service: str, audit_info: AWS_Audit_Info) -> dict: - regional_clients = {} - # Get json locally - actual_directory = os.path.dirname(os.path.realpath(__file__)) - f = open_file(f"{actual_directory}/{aws_services_json_file}") - data = parse_json_file(f) - # Check if it is a subservice - if service == "accessanalyzer": - json_regions = data["services"]["iam"]["regions"][audit_info.audited_partition] - elif service == "apigatewayv2": - json_regions = data["services"]["apigateway"]["regions"][ - audit_info.audited_partition - ] - elif service == "macie2": - json_regions = data["services"]["macie"]["regions"][ - audit_info.audited_partition - ] - elif service == "logs": - json_regions = data["services"]["cloudwatch"]["regions"][ - audit_info.audited_partition - ] - elif service == "dax": - json_regions = data["services"]["dynamodb"]["regions"][ - audit_info.audited_partition - ] - elif service == "glacier": - json_regions = data["services"]["s3"]["regions"][audit_info.audited_partition] - elif service == "opensearch": - json_regions = data["services"]["es"]["regions"][audit_info.audited_partition] - elif service == "elbv2": - json_regions = data["services"]["elb"]["regions"][audit_info.audited_partition] - elif service == "wafv2" or service == "waf-regional": - json_regions = data["services"]["waf"]["regions"][audit_info.audited_partition] - else: +def generate_regional_clients( + service: str, audit_info: AWS_Audit_Info, global_service: bool = False +) -> dict: + try: + regional_clients = {} + # Get json locally + actual_directory = os.path.dirname(os.path.realpath(__file__)) + f = open_file(f"{actual_directory}/{aws_services_json_file}") + data = parse_json_file(f) + # Check if it is a subservice json_regions = data["services"][service]["regions"][ audit_info.audited_partition ] - if audit_info.audited_regions: # Check for input aws audit_info.audited_regions - regions = list( - set(json_regions).intersection(audit_info.audited_regions) - ) # Get common regions between input and json - else: # Get all regions from json of the service and partition - regions = json_regions - for region in regions: - regional_client = audit_info.audit_session.client(service, region_name=region) - regional_client.region = region - regional_clients[region] = regional_client - return regional_clients - - -def get_region_global_service(audit_info: AWS_Audit_Info) -> str: - # Check if global service to send the finding to first audited region - if audit_info.audited_regions: - return audit_info.audited_regions[0] - return audit_info.profile_region + if audit_info.audited_regions: # Check for input aws audit_info.audited_regions + regions = list( + set(json_regions).intersection(audit_info.audited_regions) + ) # Get common regions between input and json + else: # Get all regions from json of the service and partition + regions = json_regions + # Check if it is global service to gather only one region + if global_service: + if regions: + if audit_info.profile_region in regions: + regions = [audit_info.profile_region] + regions = regions[:1] + for region in regions: + regional_client = audit_info.audit_session.client( + service, region_name=region + ) + regional_client.region = region + regional_clients[region] = regional_client + return regional_clients + except Exception as error: + logger.error( + f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) diff --git a/prowler/providers/aws/aws_regions_by_service.json b/prowler/providers/aws/aws_regions_by_service.json index 05b3a390..512adbf2 100644 --- a/prowler/providers/aws/aws_regions_by_service.json +++ b/prowler/providers/aws/aws_regions_by_service.json @@ -1,5 +1,45 @@ { "services": { + "accessanalyzer": { + "regions": { + "aws": [ + "ap-south-2", + "ap-southeast-1", + "ap-southeast-2", + "ap-southeast-3", + "eu-north-1", + "us-west-1", + "af-south-1", + "ap-northeast-1", + "ca-central-1", + "eu-central-1", + "eu-south-2", + "eu-west-2", + "me-central-1", + "me-south-1", + "us-east-2", + "us-west-2", + "ap-east-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "eu-central-2", + "eu-south-1", + "eu-west-1", + "eu-west-3", + "sa-east-1", + "us-east-1" + ], + "aws-cn": [ + "cn-north-1", + "cn-northwest-1" + ], + "aws-us-gov": [ + "us-gov-east-1", + "us-gov-west-1" + ] + } + }, "acm": { "regions": { "aws": [ @@ -211,6 +251,46 @@ ] } }, + "apigatewayv2": { + "regions": { + "aws": [ + "af-south-1", + "ap-east-1", + "ap-northeast-3", + "eu-central-1", + "eu-north-1", + "eu-south-2", + "sa-east-1", + "us-east-1", + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-2", + "ap-southeast-3", + "eu-south-1", + "eu-west-1", + "eu-west-2", + "me-central-1", + "us-east-2", + "ap-south-1", + "ap-south-2", + "ap-southeast-1", + "ca-central-1", + "eu-central-2", + "eu-west-3", + "me-south-1", + "us-west-1", + "us-west-2" + ], + "aws-cn": [ + "cn-northwest-1", + "cn-north-1" + ], + "aws-us-gov": [ + "us-gov-east-1", + "us-gov-west-1" + ] + } + }, "appflow": { "regions": { "aws": [ @@ -1567,6 +1647,46 @@ ] } }, + "dax": { + "regions": { + "aws": [ + "ap-south-2", + "ap-southeast-3", + "ca-central-1", + "eu-central-1", + "eu-central-2", + "eu-north-1", + "eu-west-2", + "me-south-1", + "ap-east-1", + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-2", + "eu-south-2", + "eu-west-1", + "me-central-1", + "us-east-2", + "us-west-2", + "af-south-1", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "eu-south-1", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-west-1" + ], + "aws-cn": [ + "cn-north-1", + "cn-northwest-1" + ], + "aws-us-gov": [ + "us-gov-east-1", + "us-gov-west-1" + ] + } + }, "deepcomposer": { "regions": { "aws": [ @@ -2271,6 +2391,46 @@ ] } }, + "elbv2": { + "regions": { + "aws": [ + "ap-northeast-1", + "ap-south-2", + "eu-north-1", + "eu-south-1", + "eu-west-2", + "me-central-1", + "me-south-1", + "us-east-2", + "us-west-2", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-3", + "eu-central-1", + "eu-central-2", + "eu-west-1", + "eu-west-3", + "sa-east-1", + "us-east-1", + "af-south-1", + "ap-east-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-south-2", + "us-west-1" + ], + "aws-cn": [ + "cn-north-1", + "cn-northwest-1" + ], + "aws-us-gov": [ + "us-gov-east-1", + "us-gov-west-1" + ] + } + }, "emr": { "regions": { "aws": [ @@ -2769,6 +2929,46 @@ "aws-us-gov": {} } }, + "glacier": { + "regions": { + "aws": [ + "ap-east-1", + "ap-northeast-1", + "eu-central-1", + "eu-west-2", + "eu-west-3", + "me-central-1", + "sa-east-1", + "us-west-1", + "ap-northeast-3", + "ap-south-1", + "ap-south-2", + "ap-southeast-1", + "ap-southeast-2", + "ap-southeast-3", + "eu-south-2", + "us-east-1", + "af-south-1", + "ap-northeast-2", + "ca-central-1", + "eu-central-2", + "eu-north-1", + "eu-south-1", + "eu-west-1", + "me-south-1", + "us-east-2", + "us-west-2" + ], + "aws-cn": [ + "cn-north-1", + "cn-northwest-1" + ], + "aws-us-gov": [ + "us-gov-east-1", + "us-gov-west-1" + ] + } + }, "globalaccelerator": { "regions": { "aws": [ @@ -3511,19 +3711,20 @@ "lex-runtime": { "regions": { "aws": [ + "af-south-1", "ap-northeast-1", + "ap-northeast-2", "ap-southeast-1", - "ap-southeast-2", + "ca-central-1", "eu-central-1", "eu-west-1", "eu-west-2", "us-east-1", - "us-west-2" + "us-west-2", + "ap-southeast-2" ], "aws-cn": {}, - "aws-us-gov": [ - "us-gov-west-1" - ] + "aws-us-gov": {} } }, "license-manager": { @@ -3584,6 +3785,46 @@ "aws-us-gov": {} } }, + "logs": { + "regions": { + "aws": [ + "ap-northeast-2", + "ap-south-2", + "ca-central-1", + "me-central-1", + "me-south-1", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + "af-south-1", + "ap-east-1", + "ap-northeast-1", + "ap-northeast-3", + "ap-southeast-3", + "eu-central-2", + "eu-north-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-south-1", + "eu-south-2", + "eu-west-1", + "eu-west-2", + "eu-west-3" + ], + "aws-cn": [ + "cn-north-1", + "cn-northwest-1" + ], + "aws-us-gov": [ + "us-gov-west-1", + "us-gov-east-1" + ] + } + }, "lookoutmetrics": { "regions": { "aws": [ @@ -3691,6 +3932,35 @@ "aws-us-gov": {} } }, + "macie2": { + "regions": { + "aws": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "af-south-1", + "ap-east-1", + "ap-northeast-3", + "ca-central-1", + "eu-west-2", + "eu-west-3", + "me-south-1", + "sa-east-1", + "us-west-1", + "us-west-2", + "eu-south-1" + ], + "aws-cn": {}, + "aws-us-gov": {} + } + }, "managedblockchain": { "regions": { "aws": [ @@ -4113,7 +4383,7 @@ "ap-northeast-3", "ap-southeast-1", "ap-southeast-2", - "eu-west-2", + "ap-southeast-3", "eu-west-3", "me-south-1", "us-east-2", @@ -4122,11 +4392,12 @@ "ap-northeast-2", "ap-south-1", "ca-central-1", - "eu-central-1", "eu-north-1", "eu-south-1", "eu-west-1", + "eu-west-2", "us-east-1", + "eu-central-1", "sa-east-1", "us-west-2" ], @@ -4151,6 +4422,46 @@ "aws-us-gov": {} } }, + "opensearch": { + "regions": { + "aws": [ + "ap-east-1", + "ap-south-1", + "ap-southeast-2", + "eu-south-1", + "eu-south-2", + "eu-west-3", + "us-west-1", + "us-west-2", + "af-south-1", + "ap-south-2", + "ca-central-1", + "eu-central-1", + "eu-central-2", + "eu-north-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-southeast-1", + "ap-southeast-3", + "eu-west-2", + "me-central-1", + "me-south-1", + "sa-east-1" + ], + "aws-cn": [ + "cn-north-1", + "cn-northwest-1" + ], + "aws-us-gov": [ + "us-gov-west-1", + "us-gov-east-1" + ] + } + }, "opsworks": { "regions": { "aws": [ @@ -4762,6 +5073,43 @@ "aws-us-gov": {} } }, + "route53domains": { + "regions": { + "aws": [ + "ap-east-1", + "ap-northeast-3", + "ap-south-1", + "eu-central-1", + "eu-central-2", + "eu-west-1", + "eu-west-2", + "sa-east-1", + "us-west-1", + "af-south-1", + "ap-northeast-2", + "ap-south-2", + "ap-southeast-1", + "ap-southeast-2", + "ap-southeast-3", + "eu-north-1", + "eu-south-1", + "eu-south-2", + "eu-west-3", + "ap-northeast-1", + "ca-central-1", + "me-central-1", + "me-south-1", + "us-east-1", + "us-east-2", + "us-west-2" + ], + "aws-cn": [ + "cn-north-1", + "cn-northwest-1" + ], + "aws-us-gov": {} + } + }, "s3": { "regions": { "aws": [ @@ -4802,6 +5150,46 @@ ] } }, + "s3control": { + "regions": { + "aws": [ + "ap-east-1", + "ap-northeast-1", + "eu-central-1", + "eu-west-2", + "eu-west-3", + "me-central-1", + "sa-east-1", + "us-west-1", + "ap-northeast-3", + "ap-south-1", + "ap-south-2", + "ap-southeast-1", + "ap-southeast-2", + "ap-southeast-3", + "eu-south-2", + "us-east-1", + "af-south-1", + "ap-northeast-2", + "ca-central-1", + "eu-central-2", + "eu-north-1", + "eu-south-1", + "eu-west-1", + "me-south-1", + "us-east-2", + "us-west-2" + ], + "aws-cn": [ + "cn-north-1", + "cn-northwest-1" + ], + "aws-us-gov": [ + "us-gov-east-1", + "us-gov-west-1" + ] + } + }, "sagemaker": { "regions": { "aws": [ @@ -5004,6 +5392,7 @@ "ap-southeast-1", "ap-southeast-2", "eu-south-1", + "eu-south-2", "eu-west-2", "eu-west-3", "us-west-1", @@ -5013,14 +5402,16 @@ "ca-central-1", "eu-central-1", "eu-north-1", - "me-south-1", "us-east-2", "us-west-2", "ap-east-1", "ap-northeast-2", "ap-south-1", + "ap-south-2", + "eu-central-2", "eu-west-1", "me-central-1", + "me-south-1", "sa-east-1", "us-east-1" ], @@ -5029,8 +5420,8 @@ "cn-northwest-1" ], "aws-us-gov": [ - "us-gov-west-1", - "us-gov-east-1" + "us-gov-east-1", + "us-gov-west-1" ] } }, @@ -5915,6 +6306,78 @@ ] } }, + "waf-regional": { + "regions": { + "aws": [ + "af-south-1", + "ap-east-1", + "ap-southeast-3", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "me-south-1", + "us-west-1", + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-southeast-1", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + "ap-south-1", + "ap-southeast-2", + "ca-central-1", + "eu-south-1", + "eu-west-3" + ], + "aws-cn": [ + "cn-north-1", + "cn-northwest-1" + ], + "aws-us-gov": [ + "us-gov-west-1", + "us-gov-east-1" + ] + } + }, + "wafv2": { + "regions": { + "aws": [ + "af-south-1", + "ap-east-1", + "ap-southeast-3", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "me-south-1", + "us-west-1", + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-southeast-1", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + "ap-south-1", + "ap-southeast-2", + "ca-central-1", + "eu-south-1", + "eu-west-3" + ], + "aws-cn": [ + "cn-north-1", + "cn-northwest-1" + ], + "aws-us-gov": [ + "us-gov-west-1", + "us-gov-east-1" + ] + } + }, "wam": { "regions": { "aws": [ diff --git a/prowler/providers/aws/services/account/account_service.py b/prowler/providers/aws/services/account/account_service.py index 88a0f91a..d0d1958d 100644 --- a/prowler/providers/aws/services/account/account_service.py +++ b/prowler/providers/aws/services/account/account_service.py @@ -1,5 +1,4 @@ ################## Account -from prowler.providers.aws.aws_provider import get_region_global_service class Account: @@ -7,7 +6,7 @@ class Account: self.service = "account" self.session = audit_info.audit_session self.audited_account = audit_info.audited_account - self.region = get_region_global_service(audit_info) + self.region = audit_info.profile_region def __get_session__(self): return self.session diff --git a/prowler/providers/aws/services/cloudfront/cloudfront_service.py b/prowler/providers/aws/services/cloudfront/cloudfront_service.py index c4ade26a..21c4a654 100644 --- a/prowler/providers/aws/services/cloudfront/cloudfront_service.py +++ b/prowler/providers/aws/services/cloudfront/cloudfront_service.py @@ -2,7 +2,7 @@ from dataclasses import dataclass from enum import Enum from prowler.lib.logger import logger -from prowler.providers.aws.aws_provider import get_region_global_service +from prowler.providers.aws.aws_provider import generate_regional_clients ################## CloudFront @@ -11,12 +11,17 @@ class CloudFront: self.service = "cloudfront" self.session = audit_info.audit_session self.audited_account = audit_info.audited_account - self.client = self.session.client(self.service) - self.region = get_region_global_service(audit_info) - self.distributions = self.__list_distributions__(self.client, self.region) - self.distributions = self.__get_distribution_config__( - self.client, self.distributions, self.region + global_client = generate_regional_clients( + self.service, audit_info, global_service=True ) + self.distributions = {} + if global_client: + self.client = list(global_client.values())[0] + self.region = self.client.region + self.distributions = self.__list_distributions__(self.client, self.region) + self.distributions = self.__get_distribution_config__( + self.client, self.distributions, self.region + ) def __get_session__(self): return self.session diff --git a/prowler/providers/aws/services/cloudtrail/cloudtrail_service.py b/prowler/providers/aws/services/cloudtrail/cloudtrail_service.py index 998eea30..5881a4ca 100644 --- a/prowler/providers/aws/services/cloudtrail/cloudtrail_service.py +++ b/prowler/providers/aws/services/cloudtrail/cloudtrail_service.py @@ -3,10 +3,7 @@ import threading from dataclasses import dataclass from prowler.lib.logger import logger -from prowler.providers.aws.aws_provider import ( - generate_regional_clients, - get_region_global_service, -) +from prowler.providers.aws.aws_provider import generate_regional_clients ################### CLOUDTRAIL @@ -15,7 +12,7 @@ class Cloudtrail: self.service = "cloudtrail" self.session = audit_info.audit_session self.audited_account = audit_info.audited_account - self.region = get_region_global_service(audit_info) + self.region = audit_info.profile_region self.regional_clients = generate_regional_clients(self.service, audit_info) self.trails = [] self.__threading_call__(self.__get_trails__) diff --git a/prowler/providers/aws/services/globalaccelerator/globalaccelerator_service.py b/prowler/providers/aws/services/globalaccelerator/globalaccelerator_service.py index b0526b8e..9af279a2 100644 --- a/prowler/providers/aws/services/globalaccelerator/globalaccelerator_service.py +++ b/prowler/providers/aws/services/globalaccelerator/globalaccelerator_service.py @@ -9,13 +9,14 @@ class GlobalAccelerator: self.service = "globalaccelerator" self.session = audit_info.audit_session self.audited_account = audit_info.audited_account - # Global Accelerator is a global service that supports endpoints in multiple AWS Regions - # but you must specify the US West (Oregon) Region to create, update, or otherwise work with accelerators. - # That is, for example, specify --region us-west-2 on AWS CLI commands. - self.region = "us-west-2" - self.client = self.session.client(self.service, self.region) self.accelerators = {} - self.__list_accelerators__() + if audit_info.audited_partition == "aws": + # Global Accelerator is a global service that supports endpoints in multiple AWS Regions + # but you must specify the US West (Oregon) Region to create, update, or otherwise work with accelerators. + # That is, for example, specify --region us-west-2 on AWS CLI commands. + self.region = "us-west-2" + self.client = self.session.client(self.service, self.region) + self.__list_accelerators__() def __get_session__(self): return self.session diff --git a/prowler/providers/aws/services/iam/iam_service.py b/prowler/providers/aws/services/iam/iam_service.py index 41c4d7c8..c26a0b34 100644 --- a/prowler/providers/aws/services/iam/iam_service.py +++ b/prowler/providers/aws/services/iam/iam_service.py @@ -3,7 +3,7 @@ from dataclasses import dataclass from datetime import datetime from prowler.lib.logger import logger -from prowler.providers.aws.aws_provider import get_region_global_service +from prowler.providers.aws.aws_provider import generate_regional_clients ################## IAM @@ -14,7 +14,11 @@ class IAM: self.account = audit_info.audited_account self.partition = audit_info.audited_partition self.client = self.session.client(self.service) - self.region = get_region_global_service(audit_info) + global_client = generate_regional_clients( + self.service, audit_info, global_service=True + ) + self.client = list(global_client.values())[0] + self.region = self.client.region self.users = self.__get_users__() self.roles = self.__get_roles__() self.account_summary = self.__get_account_summary__() diff --git a/prowler/providers/aws/services/route53/route53_service.py b/prowler/providers/aws/services/route53/route53_service.py index f633908a..828933f8 100644 --- a/prowler/providers/aws/services/route53/route53_service.py +++ b/prowler/providers/aws/services/route53/route53_service.py @@ -1,7 +1,7 @@ from pydantic import BaseModel from prowler.lib.logger import logger -from prowler.providers.aws.aws_provider import get_region_global_service +from prowler.providers.aws.aws_provider import generate_regional_clients ################## Route53 @@ -10,11 +10,15 @@ class Route53: self.service = "route53" self.session = audit_info.audit_session self.audited_partition = audit_info.audited_partition - self.client = self.session.client(self.service) - self.region = get_region_global_service(audit_info) self.hosted_zones = {} - self.__list_hosted_zones__() - self.__list_query_logging_configs__() + global_client = generate_regional_clients( + self.service, audit_info, global_service=True + ) + if global_client: + self.client = list(global_client.values())[0] + self.region = self.client.region + self.__list_hosted_zones__() + self.__list_query_logging_configs__() def __get_session__(self): return self.session @@ -84,13 +88,14 @@ class Route53Domains: self.service = "route53domains" self.session = audit_info.audit_session self.audited_account = audit_info.audited_account - # Route53Domains is a global service that supports endpoints in multiple AWS Regions - # but you must specify the US East (N. Virginia) Region to create, update, or otherwise work with domains. - self.region = "us-east-1" - self.client = self.session.client(self.service, self.region) self.domains = {} - self.__list_domains__() - self.__get_domain_detail__() + if audit_info.audited_partition == "aws": + # Route53Domains is a global service that supports endpoints in multiple AWS Regions + # but you must specify the US East (N. Virginia) Region to create, update, or otherwise work with domains. + self.region = "us-east-1" + self.client = self.session.client(self.service, self.region) + self.__list_domains__() + self.__get_domain_detail__() def __get_session__(self): return self.session diff --git a/prowler/providers/aws/services/s3/s3_service.py b/prowler/providers/aws/services/s3/s3_service.py index cae2c501..9c05d130 100644 --- a/prowler/providers/aws/services/s3/s3_service.py +++ b/prowler/providers/aws/services/s3/s3_service.py @@ -3,10 +3,7 @@ import threading from dataclasses import dataclass from prowler.lib.logger import logger -from prowler.providers.aws.aws_provider import ( - generate_regional_clients, - get_region_global_service, -) +from prowler.providers.aws.aws_provider import generate_regional_clients ################## S3 @@ -203,9 +200,13 @@ class S3Control: self.service = "s3control" self.session = audit_info.audit_session self.audited_account = audit_info.audited_account - self.region = get_region_global_service(audit_info) - self.client = self.session.client(self.service, self.region) - self.account_public_access_block = self.__get_public_access_block__() + global_client = generate_regional_clients( + self.service, audit_info, global_service=True + ) + if global_client: + self.client = list(global_client.values())[0] + self.region = self.client.region + self.account_public_access_block = self.__get_public_access_block__() def __get_session__(self): return self.session diff --git a/prowler/providers/aws/services/shield/check_extra7170 b/prowler/providers/aws/services/shield/check_extra7170 deleted file mode 100644 index f8675a5c..00000000 --- a/prowler/providers/aws/services/shield/check_extra7170 +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env bash - -# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy -# of the License at http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software distributed -# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -# CONDITIONS OF ANY KIND, either express or implied. See the License for the -# specific language governing permissions and limitations under the License. - -CHECK_ID_extra7170="7.170" -CHECK_TITLE_extra7170="[extra7170] Check if internet-facing application load balancers are protected by AWS Shield Advanced" -CHECK_SCORED_extra7170="NOT_SCORED" -CHECK_CIS_LEVEL_extra7170="EXTRA" -CHECK_SEVERITY_extra7170="Medium" -CHECK_ASFF_RESOURCE_TYPE_extra7170="AwsElasticLoadBalancingV2LoadBalancer" -CHECK_ALTERNATE_check7170="extra7170" -CHECK_SERVICENAME_extra7170="shield" -CHECK_RISK_extra7170='AWS Shield Advanced provides expanded DDoS attack protection for your resources' -CHECK_REMEDIATION_extra7170='Add as a protected resource in AWS Shield Advanced.' -CHECK_DOC_extra7170='https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html' -CHECK_CAF_EPIC_extra7170='Infrastructure security' - -extra7170() { - if [[ "$($AWSCLI $PROFILE_OPT shield get-subscription-state --output text)" == "ACTIVE" ]]; then - for regx in $REGIONS; do - LIST_OF_APPLICATION_LOAD_BALANCERS=$($AWSCLI elbv2 describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancers[?Type == `application` && Scheme == `internet-facing`].[LoadBalancerName,LoadBalancerArn]' --output text) - if [[ $LIST_OF_APPLICATION_LOAD_BALANCERS ]]; then - while read -r alb; do - ALB_NAME=$(echo $alb | awk '{ print $1; }') - ALB_ARN=$(echo $alb | awk '{ print $2; }') - if $AWSCLI $PROFILE_OPT shield describe-protection --resource-arn $ALB_ARN >/dev/null 2>&1; then - textPass "$regx: ALB $ALB_NAME is protected by AWS Shield Advanced" "$regx" "$ALB_NAME" - else - textFail "$regx: ALB $ALB_NAME is not protected by AWS Shield Advanced" "$regx" "$ALB_NAME" - fi - done <<<"$LIST_OF_APPLICATION_LOAD_BALANCERS" - else - textInfo "$regx: no application load balancers found" "$regx" - fi - done - else - textInfo "No AWS Shield Advanced subscription found. Skipping check." - fi -} diff --git a/prowler/providers/aws/services/shield/shield_service.py b/prowler/providers/aws/services/shield/shield_service.py index 4d1eb187..35efe5f1 100644 --- a/prowler/providers/aws/services/shield/shield_service.py +++ b/prowler/providers/aws/services/shield/shield_service.py @@ -1,7 +1,7 @@ from pydantic import BaseModel from prowler.lib.logger import logger -from prowler.providers.aws.aws_provider import get_region_global_service +from prowler.providers.aws.aws_provider import generate_regional_clients ################### Shield @@ -10,11 +10,17 @@ class Shield: self.service = "shield" self.session = audit_info.audit_session self.audited_account = audit_info.audited_account - self.client = self.session.client(self.service) - self.region = get_region_global_service(audit_info) - self.enabled = self.__get_subscription_state__() + global_client = generate_regional_clients( + self.service, audit_info, global_service=True + ) self.protections = {} - self.__list_protections__() + self.enabled = False + if global_client: + self.client = list(global_client.values())[0] + self.region = self.client.region + self.enabled = self.__get_subscription_state__() + if self.enabled: + self.__list_protections__() def __get_session__(self): return self.session diff --git a/prowler/providers/aws/services/trustedadvisor/trustedadvisor_errors_and_warnings/trustedadvisor_errors_and_warnings.py b/prowler/providers/aws/services/trustedadvisor/trustedadvisor_errors_and_warnings/trustedadvisor_errors_and_warnings.py index 6f8d33ef..99608da1 100644 --- a/prowler/providers/aws/services/trustedadvisor/trustedadvisor_errors_and_warnings/trustedadvisor_errors_and_warnings.py +++ b/prowler/providers/aws/services/trustedadvisor/trustedadvisor_errors_and_warnings/trustedadvisor_errors_and_warnings.py @@ -7,17 +7,22 @@ from prowler.providers.aws.services.trustedadvisor.trustedadvisor_client import class trustedadvisor_errors_and_warnings(Check): def execute(self): findings = [] - if trustedadvisor_client.checks: - for check in trustedadvisor_client.checks: - report = Check_Report_AWS(self.metadata()) - report.region = check.region - report.resource_id = check.id - report.status = "FAIL" - report.status_extended = ( - f"Trusted Advisor check {check.name} is in state {check.status}." - ) - if check.status == "ok": - report.status = "PASS" - findings.append(report) + if trustedadvisor_client.enabled: + if trustedadvisor_client.checks: + for check in trustedadvisor_client.checks: + report = Check_Report_AWS(self.metadata()) + report.region = check.region + report.resource_id = check.id + report.status = "FAIL" + report.status_extended = f"Trusted Advisor check {check.name} is in state {check.status}." + if check.status == "ok": + report.status = "PASS" + findings.append(report) + else: + report = Check_Report_AWS(self.metadata()) + report.status = "INFO" + report.status_extended = "Amazon Web Services Premium Support Subscription is required to use this service." + report.resource_id = trustedadvisor_client.account + findings.append(report) return findings diff --git a/prowler/providers/aws/services/trustedadvisor/trustedadvisor_service.py b/prowler/providers/aws/services/trustedadvisor/trustedadvisor_service.py index e2601d37..f745f381 100644 --- a/prowler/providers/aws/services/trustedadvisor/trustedadvisor_service.py +++ b/prowler/providers/aws/services/trustedadvisor/trustedadvisor_service.py @@ -1,10 +1,9 @@ -import threading from typing import Optional +from botocore.client import ClientError from pydantic import BaseModel from prowler.lib.logger import logger -from prowler.providers.aws.aws_provider import generate_regional_clients ################################ TrustedAdvisor @@ -12,54 +11,64 @@ class TrustedAdvisor: def __init__(self, audit_info): self.service = "support" self.session = audit_info.audit_session - self.regional_clients = generate_regional_clients(self.service, audit_info) + self.account = audit_info.audited_account self.checks = [] - self.__threading_call__(self.__describe_trusted_advisor_checks__) - self.__threading_call__(self.__describe_trusted_advisor_check_result__) + self.enabled = True + # Support API is not available in China Partition + # But only in us-east-1 or us-gov-west-1 https://docs.aws.amazon.com/general/latest/gr/awssupport.html + if audit_info.audited_partition != "aws-cn": + if audit_info.audited_partition == "aws": + support_region = "us-east-1" + else: + support_region = "us-gov-west-1" + self.client = audit_info.audit_session.client( + self.service, region_name=support_region + ) + self.client.region = self.region = support_region + self.__describe_trusted_advisor_checks__() + self.__describe_trusted_advisor_check_result__() def __get_session__(self): return self.session - def __threading_call__(self, call): - threads = [] - for regional_client in self.regional_clients.values(): - threads.append(threading.Thread(target=call, args=(regional_client,))) - for t in threads: - t.start() - for t in threads: - t.join() - - def __describe_trusted_advisor_checks__(self, regional_client): + def __describe_trusted_advisor_checks__(self): logger.info("TrustedAdvisor - Describing Checks...") try: - for check in regional_client.describe_trusted_advisor_checks(language="en")[ + for check in self.client.describe_trusted_advisor_checks(language="en")[ "checks" ]: self.checks.append( Check( id=check["id"], name=check["name"], - region=regional_client.region, + region=self.client.region, ) ) + except ClientError as error: + if error.response["Error"]["Code"] == "SubscriptionRequiredException": + self.enabled = False + else: + logger.error( + f"{self.client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) except Exception as error: logger.error( - f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + f"{self.client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" ) - def __describe_trusted_advisor_check_result__(self, regional_client): + def __describe_trusted_advisor_check_result__(self): logger.info("TrustedAdvisor - Describing Check Result...") try: for check in self.checks: - if check.region == regional_client.region: - response = regional_client.describe_trusted_advisor_check_result( + if check.region == self.client.region: + response = self.client.describe_trusted_advisor_check_result( checkId=check.id ) if "result" in response: check.status = response["result"]["status"] except Exception as error: logger.error( - f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + f"{self.client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" ) diff --git a/tests/providers/aws/aws_provider_test.py b/tests/providers/aws/aws_provider_test.py index 3d1efafc..5a850b0d 100644 --- a/tests/providers/aws/aws_provider_test.py +++ b/tests/providers/aws/aws_provider_test.py @@ -2,7 +2,7 @@ import boto3 import sure # noqa from moto import mock_iam, mock_sts -from prowler.providers.aws.aws_provider import assume_role, get_region_global_service +from prowler.providers.aws.aws_provider import assume_role, generate_regional_clients from prowler.providers.aws.lib.audit_info.models import AWS_Assume_Role, AWS_Audit_Info ACCOUNT_ID = 123456789012 @@ -82,24 +82,85 @@ class Test_AWS_Provider: 21 + 1 + len(sessionName) ) - def test_get_region_global_service(self): - # Create mock audit_info - input_audit_info = AWS_Audit_Info( + def test_generate_regional_clients(self): + # New Boto3 session with the previously create user + session = boto3.session.Session( + region_name="us-east-1", + ) + audited_regions = ["eu-west-1", "us-east-1"] + # Fulfil the input session object for Prowler + audit_info = AWS_Audit_Info( original_session=None, - audit_session=None, - audited_account="123456789012", - audited_identity_arn="test-arn", - audited_user_id="test", + audit_session=session, + audited_account=None, audited_partition="aws", - profile="default", - profile_region="eu-west-1", + audited_identity_arn=None, + audited_user_id=None, + profile=None, + profile_region=None, credentials=None, assumed_role_info=None, - audited_regions=["eu-west-2", "eu-west-1"], + audited_regions=audited_regions, organizations_metadata=None, ) - - assert ( - get_region_global_service(input_audit_info) - == input_audit_info.audited_regions[0] + generate_regional_clients_response = generate_regional_clients( + "ec2", audit_info ) + + assert set(generate_regional_clients_response.keys()) == set(audited_regions) + + def test_generate_regional_clients_global_service(self): + # New Boto3 session with the previously create user + session = boto3.session.Session( + region_name="us-east-1", + ) + audited_regions = ["eu-west-1", "us-east-1"] + profile_region = "us-east-1" + # Fulfil the input session object for Prowler + audit_info = AWS_Audit_Info( + original_session=None, + audit_session=session, + audited_account=None, + audited_partition="aws", + audited_identity_arn=None, + audited_user_id=None, + profile=None, + profile_region=profile_region, + credentials=None, + assumed_role_info=None, + audited_regions=audited_regions, + organizations_metadata=None, + ) + generate_regional_clients_response = generate_regional_clients( + "route53", audit_info, global_service=True + ) + + assert list(generate_regional_clients_response.keys()) == [profile_region] + + def test_generate_regional_clients_cn_partition(self): + # New Boto3 session with the previously create user + session = boto3.session.Session( + region_name="us-east-1", + ) + audited_regions = ["cn-northwest-1", "cn-north-1"] + # Fulfil the input session object for Prowler + audit_info = AWS_Audit_Info( + original_session=None, + audit_session=session, + audited_account=None, + audited_partition="aws-cn", + audited_identity_arn=None, + audited_user_id=None, + profile=None, + profile_region=None, + credentials=None, + assumed_role_info=None, + audited_regions=audited_regions, + organizations_metadata=None, + ) + generate_regional_clients_response = generate_regional_clients( + "shield", audit_info, global_service=True + ) + + # Shield does not exist in China + assert generate_regional_clients_response == {} diff --git a/tests/providers/aws/services/iam/iam_administrator_access_with_mfa/iam_administrator_access_with_mfa_test.py b/tests/providers/aws/services/iam/iam_administrator_access_with_mfa/iam_administrator_access_with_mfa_test.py index 731099ce..19d9dc7f 100644 --- a/tests/providers/aws/services/iam/iam_administrator_access_with_mfa/iam_administrator_access_with_mfa_test.py +++ b/tests/providers/aws/services/iam/iam_administrator_access_with_mfa/iam_administrator_access_with_mfa_test.py @@ -17,6 +17,8 @@ class Test_iam_administrator_access_with_mfa_test: from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info from prowler.providers.aws.services.iam.iam_service import IAM + current_audit_info.audited_partition = "aws" + with mock.patch( "prowler.providers.aws.services.iam.iam_administrator_access_with_mfa.iam_administrator_access_with_mfa.iam_client", new=IAM(current_audit_info), @@ -55,6 +57,8 @@ class Test_iam_administrator_access_with_mfa_test: from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info from prowler.providers.aws.services.iam.iam_service import IAM + current_audit_info.audited_partition = "aws" + with mock.patch( "prowler.providers.aws.services.iam.iam_administrator_access_with_mfa.iam_administrator_access_with_mfa.iam_client", new=IAM(current_audit_info), @@ -88,6 +92,8 @@ class Test_iam_administrator_access_with_mfa_test: from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info from prowler.providers.aws.services.iam.iam_service import IAM + current_audit_info.audited_partition = "aws" + with mock.patch( "prowler.providers.aws.services.iam.iam_administrator_access_with_mfa.iam_administrator_access_with_mfa.iam_client", new=IAM(current_audit_info), @@ -123,6 +129,8 @@ class Test_iam_administrator_access_with_mfa_test: from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info from prowler.providers.aws.services.iam.iam_service import IAM + current_audit_info.audited_partition = "aws" + with mock.patch( "prowler.providers.aws.services.iam.iam_administrator_access_with_mfa.iam_administrator_access_with_mfa.iam_client", new=IAM(current_audit_info), @@ -182,6 +190,8 @@ class Test_iam_administrator_access_with_mfa_test: from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info from prowler.providers.aws.services.iam.iam_service import IAM + current_audit_info.audited_partition = "aws" + with mock.patch( "prowler.providers.aws.services.iam.iam_administrator_access_with_mfa.iam_administrator_access_with_mfa.iam_client", new=IAM(current_audit_info), diff --git a/tests/providers/aws/services/iam/iam_avoid_root_usage/iam_avoid_root_usage_test.py b/tests/providers/aws/services/iam/iam_avoid_root_usage/iam_avoid_root_usage_test.py index e93ada74..d4caae56 100644 --- a/tests/providers/aws/services/iam/iam_avoid_root_usage/iam_avoid_root_usage_test.py +++ b/tests/providers/aws/services/iam/iam_avoid_root_usage/iam_avoid_root_usage_test.py @@ -18,6 +18,8 @@ class Test_iam_avoid_root_usage: from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info from prowler.providers.aws.services.iam.iam_service import IAM + current_audit_info.audited_partition = "aws" + with mock.patch( "prowler.providers.aws.services.iam.iam_avoid_root_usage.iam_avoid_root_usage.iam_client", new=IAM(current_audit_info), @@ -51,6 +53,8 @@ class Test_iam_avoid_root_usage: from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info from prowler.providers.aws.services.iam.iam_service import IAM + current_audit_info.audited_partition = "aws" + with mock.patch( "prowler.providers.aws.services.iam.iam_avoid_root_usage.iam_avoid_root_usage.iam_client", new=IAM(current_audit_info), @@ -83,6 +87,8 @@ class Test_iam_avoid_root_usage: from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info from prowler.providers.aws.services.iam.iam_service import IAM + current_audit_info.audited_partition = "aws" + with mock.patch( "prowler.providers.aws.services.iam.iam_avoid_root_usage.iam_avoid_root_usage.iam_client", new=IAM(current_audit_info), @@ -115,6 +121,8 @@ class Test_iam_avoid_root_usage: from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info from prowler.providers.aws.services.iam.iam_service import IAM + current_audit_info.audited_partition = "aws" + with mock.patch( "prowler.providers.aws.services.iam.iam_avoid_root_usage.iam_avoid_root_usage.iam_client", new=IAM(current_audit_info), diff --git a/tests/providers/aws/services/iam/iam_check_saml_providers_sts/iam_check_saml_providers_sts_test.py b/tests/providers/aws/services/iam/iam_check_saml_providers_sts/iam_check_saml_providers_sts_test.py index 0b80f879..d665e433 100644 --- a/tests/providers/aws/services/iam/iam_check_saml_providers_sts/iam_check_saml_providers_sts_test.py +++ b/tests/providers/aws/services/iam/iam_check_saml_providers_sts/iam_check_saml_providers_sts_test.py @@ -44,6 +44,8 @@ nTTxU4a7x1naFxzYXK1iQ1vMARKMjDb19QEJIEJKZlDK4uS7yMlf1nFS from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info from prowler.providers.aws.services.iam.iam_service import IAM + current_audit_info.audited_partition = "aws" + with mock.patch( "prowler.providers.aws.services.iam.iam_check_saml_providers_sts.iam_check_saml_providers_sts.iam_client", new=IAM(current_audit_info), diff --git a/tests/providers/aws/services/iam/iam_disable_30_days_credentials/iam_disable_30_days_credentials_test.py b/tests/providers/aws/services/iam/iam_disable_30_days_credentials/iam_disable_30_days_credentials_test.py index 21e9c668..76ecaeae 100644 --- a/tests/providers/aws/services/iam/iam_disable_30_days_credentials/iam_disable_30_days_credentials_test.py +++ b/tests/providers/aws/services/iam/iam_disable_30_days_credentials/iam_disable_30_days_credentials_test.py @@ -18,6 +18,8 @@ class Test_iam_disable_30_days_credentials_test: from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info from prowler.providers.aws.services.iam.iam_service import IAM + current_audit_info.audited_partition = "aws" + with mock.patch( "prowler.providers.aws.services.iam.iam_disable_30_days_credentials.iam_disable_30_days_credentials.iam_client", new=IAM(current_audit_info), @@ -48,6 +50,8 @@ class Test_iam_disable_30_days_credentials_test: from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info from prowler.providers.aws.services.iam.iam_service import IAM + current_audit_info.audited_partition = "aws" + with mock.patch( "prowler.providers.aws.services.iam.iam_disable_30_days_credentials.iam_disable_30_days_credentials.iam_client", new=IAM(current_audit_info), @@ -75,6 +79,8 @@ class Test_iam_disable_30_days_credentials_test: from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info from prowler.providers.aws.services.iam.iam_service import IAM + current_audit_info.audited_partition = "aws" + with mock.patch( "prowler.providers.aws.services.iam.iam_disable_30_days_credentials.iam_disable_30_days_credentials.iam_client", new=IAM(current_audit_info), diff --git a/tests/providers/aws/services/iam/iam_disable_45_days_credentials/iam_disable_45_days_credentials_test.py b/tests/providers/aws/services/iam/iam_disable_45_days_credentials/iam_disable_45_days_credentials_test.py index 5861dd25..84c3ecf5 100644 --- a/tests/providers/aws/services/iam/iam_disable_45_days_credentials/iam_disable_45_days_credentials_test.py +++ b/tests/providers/aws/services/iam/iam_disable_45_days_credentials/iam_disable_45_days_credentials_test.py @@ -18,6 +18,8 @@ class Test_iam_disable_45_days_credentials_test: from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info from prowler.providers.aws.services.iam.iam_service import IAM + current_audit_info.audited_partition = "aws" + with mock.patch( "prowler.providers.aws.services.iam.iam_disable_45_days_credentials.iam_disable_45_days_credentials.iam_client", new=IAM(current_audit_info), @@ -48,6 +50,8 @@ class Test_iam_disable_45_days_credentials_test: from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info from prowler.providers.aws.services.iam.iam_service import IAM + current_audit_info.audited_partition = "aws" + with mock.patch( "prowler.providers.aws.services.iam.iam_disable_45_days_credentials.iam_disable_45_days_credentials.iam_client", new=IAM(current_audit_info), @@ -75,6 +79,8 @@ class Test_iam_disable_45_days_credentials_test: from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info from prowler.providers.aws.services.iam.iam_service import IAM + current_audit_info.audited_partition = "aws" + with mock.patch( "prowler.providers.aws.services.iam.iam_disable_45_days_credentials.iam_disable_45_days_credentials.iam_client", new=IAM(current_audit_info), diff --git a/tests/providers/aws/services/iam/iam_disable_90_days_credentials/iam_disable_90_days_credentials_test.py b/tests/providers/aws/services/iam/iam_disable_90_days_credentials/iam_disable_90_days_credentials_test.py index 4763b2eb..99685baf 100644 --- a/tests/providers/aws/services/iam/iam_disable_90_days_credentials/iam_disable_90_days_credentials_test.py +++ b/tests/providers/aws/services/iam/iam_disable_90_days_credentials/iam_disable_90_days_credentials_test.py @@ -18,6 +18,8 @@ class Test_iam_disable_90_days_credentials_test: from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info from prowler.providers.aws.services.iam.iam_service import IAM + current_audit_info.audited_partition = "aws" + with mock.patch( "prowler.providers.aws.services.iam.iam_disable_90_days_credentials.iam_disable_90_days_credentials.iam_client", new=IAM(current_audit_info), @@ -48,6 +50,8 @@ class Test_iam_disable_90_days_credentials_test: from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info from prowler.providers.aws.services.iam.iam_service import IAM + current_audit_info.audited_partition = "aws" + with mock.patch( "prowler.providers.aws.services.iam.iam_disable_90_days_credentials.iam_disable_90_days_credentials.iam_client", new=IAM(current_audit_info), @@ -75,6 +79,8 @@ class Test_iam_disable_90_days_credentials_test: from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info from prowler.providers.aws.services.iam.iam_service import IAM + current_audit_info.audited_partition = "aws" + with mock.patch( "prowler.providers.aws.services.iam.iam_disable_90_days_credentials.iam_disable_90_days_credentials.iam_client", new=IAM(current_audit_info), diff --git a/tests/providers/aws/services/iam/iam_no_custom_policy_permissive_role_assumption/iam_no_custom_policy_permissive_role_assumption_test.py b/tests/providers/aws/services/iam/iam_no_custom_policy_permissive_role_assumption/iam_no_custom_policy_permissive_role_assumption_test.py index b49b2f64..bfb4d9bb 100644 --- a/tests/providers/aws/services/iam/iam_no_custom_policy_permissive_role_assumption/iam_no_custom_policy_permissive_role_assumption_test.py +++ b/tests/providers/aws/services/iam/iam_no_custom_policy_permissive_role_assumption/iam_no_custom_policy_permissive_role_assumption_test.py @@ -24,6 +24,8 @@ class Test_iam_no_custom_policy_permissive_role_assumption: from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info from prowler.providers.aws.services.iam.iam_service import IAM + current_audit_info.audited_partition = "aws" + with mock.patch( "prowler.providers.aws.services.iam.iam_no_custom_policy_permissive_role_assumption.iam_no_custom_policy_permissive_role_assumption.iam_client", new=IAM(current_audit_info), @@ -59,6 +61,8 @@ class Test_iam_no_custom_policy_permissive_role_assumption: from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info from prowler.providers.aws.services.iam.iam_service import IAM + current_audit_info.audited_partition = "aws" + with mock.patch( "prowler.providers.aws.services.iam.iam_no_custom_policy_permissive_role_assumption.iam_no_custom_policy_permissive_role_assumption.iam_client", new=IAM(current_audit_info), @@ -97,6 +101,8 @@ class Test_iam_no_custom_policy_permissive_role_assumption: from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info from prowler.providers.aws.services.iam.iam_service import IAM + current_audit_info.audited_partition = "aws" + with mock.patch( "prowler.providers.aws.services.iam.iam_no_custom_policy_permissive_role_assumption.iam_no_custom_policy_permissive_role_assumption.iam_client", new=IAM(current_audit_info), @@ -132,6 +138,8 @@ class Test_iam_no_custom_policy_permissive_role_assumption: from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info from prowler.providers.aws.services.iam.iam_service import IAM + current_audit_info.audited_partition = "aws" + with mock.patch( "prowler.providers.aws.services.iam.iam_no_custom_policy_permissive_role_assumption.iam_no_custom_policy_permissive_role_assumption.iam_client", new=IAM(current_audit_info), @@ -179,6 +187,8 @@ class Test_iam_no_custom_policy_permissive_role_assumption: from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info from prowler.providers.aws.services.iam.iam_service import IAM + current_audit_info.audited_partition = "aws" + with mock.patch( "prowler.providers.aws.services.iam.iam_no_custom_policy_permissive_role_assumption.iam_no_custom_policy_permissive_role_assumption.iam_client", new=IAM(current_audit_info), diff --git a/tests/providers/aws/services/iam/iam_service_test.py b/tests/providers/aws/services/iam/iam_service_test.py index 1fbc4f89..7407d873 100644 --- a/tests/providers/aws/services/iam/iam_service_test.py +++ b/tests/providers/aws/services/iam/iam_service_test.py @@ -2,11 +2,11 @@ import json from json import dumps from boto3 import client, session +from freezegun import freeze_time from moto import mock_iam from prowler.providers.aws.lib.audit_info.models import AWS_Audit_Info from prowler.providers.aws.services.iam.iam_service import IAM -from freezegun import freeze_time AWS_ACCOUNT_NUMBER = 123456789012 TEST_DATETIME = "2023-01-01T12:01:01+00:00" @@ -23,10 +23,10 @@ class Test_IAM_Service: ), audited_account=None, audited_user_id=None, - audited_partition=None, + audited_partition="aws", audited_identity_arn=None, profile=None, - profile_region=None, + profile_region="us-east-1", credentials=None, assumed_role_info=None, audited_regions=None, diff --git a/tests/providers/aws/services/trustedadvisor/trustedadvisor_errors_and_warnings/trustedadvisor_errors_and_warnings_test.py b/tests/providers/aws/services/trustedadvisor/trustedadvisor_errors_and_warnings/trustedadvisor_errors_and_warnings_test.py index e484ead3..70147bca 100644 --- a/tests/providers/aws/services/trustedadvisor/trustedadvisor_errors_and_warnings/trustedadvisor_errors_and_warnings_test.py +++ b/tests/providers/aws/services/trustedadvisor/trustedadvisor_errors_and_warnings/trustedadvisor_errors_and_warnings_test.py @@ -14,6 +14,8 @@ class Test_trustedadvisor_errors_and_warnings: def test_no_detectors(self): trustedadvisor_client = mock.MagicMock trustedadvisor_client.checks = [] + trustedadvisor_client.enabled = False + trustedadvisor_client.account = AWS_ACCOUNT_NUMBER with mock.patch( "prowler.providers.aws.services.trustedadvisor.trustedadvisor_service.TrustedAdvisor", trustedadvisor_client, @@ -24,11 +26,16 @@ class Test_trustedadvisor_errors_and_warnings: check = trustedadvisor_errors_and_warnings() result = check.execute() - assert len(result) == 0 + assert len(result) == 1 + assert ( + result[0].status_extended + == "Amazon Web Services Premium Support Subscription is required to use this service." + ) def test_trustedadvisor_all_passed_checks(self): trustedadvisor_client = mock.MagicMock trustedadvisor_client.checks = [] + trustedadvisor_client.enabled = True trustedadvisor_client.checks.append( Check( id="check1", @@ -55,6 +62,7 @@ class Test_trustedadvisor_errors_and_warnings: def test_trustedadvisor_error_check(self): trustedadvisor_client = mock.MagicMock trustedadvisor_client.checks = [] + trustedadvisor_client.enabled = True trustedadvisor_client.checks.append( Check( id="check1", diff --git a/tests/providers/aws/services/trustedadvisor/trustedadvisor_service_test.py b/tests/providers/aws/services/trustedadvisor/trustedadvisor_service_test.py index 24bfeb71..75edcdf0 100644 --- a/tests/providers/aws/services/trustedadvisor/trustedadvisor_service_test.py +++ b/tests/providers/aws/services/trustedadvisor/trustedadvisor_service_test.py @@ -21,17 +21,7 @@ def mock_make_api_call(self, operation_name, kwarg): return make_api_call(self, operation_name, kwarg) -def mock_generate_regional_clients(service, audit_info): - regional_client = audit_info.audit_session.client(service, region_name=AWS_REGION) - regional_client.region = AWS_REGION - return {AWS_REGION: regional_client} - - @patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call) -@patch( - "prowler.providers.aws.services.trustedadvisor.trustedadvisor_service.generate_regional_clients", - new=mock_generate_regional_clients, -) class Test_TrustedAdvisor_Service: # Mocked Audit Info def set_mocked_audit_info(self): @@ -46,7 +36,7 @@ class Test_TrustedAdvisor_Service: audited_partition="aws", audited_identity_arn=None, profile=None, - profile_region=None, + profile_region=AWS_REGION, credentials=None, assumed_role_info=None, audited_regions=None, @@ -64,8 +54,7 @@ class Test_TrustedAdvisor_Service: def test_client(self): audit_info = self.set_mocked_audit_info() trustedadvisor = TrustedAdvisor(audit_info) - for reg_client in trustedadvisor.regional_clients.values(): - assert reg_client.__class__.__name__ == "Support" + assert trustedadvisor.client.__class__.__name__ == "Support" # Test TrustedAdvisor session def test__get_session__(self): diff --git a/util/update_aws_services_regions.py b/util/update_aws_services_regions.py index caae230b..5731f188 100644 --- a/util/update_aws_services_regions.py +++ b/util/update_aws_services_regions.py @@ -20,7 +20,8 @@ logging.basicConfig( with request.urlopen(aws_services_json_url) as url: # Get the AWS regions matrix online logging.info(f"Downloading JSON from {aws_services_json_url}") original_matrix_regions_aws = json.loads(url.read().decode()) -parsed__matrix_regions_aws = f"{os.path.dirname(os.path.realpath(__name__))}/prowler/providers/aws/aws_regions_by_service.json" +parsed_matrix_regions_aws = f"{os.path.dirname(os.path.realpath(__name__))}/prowler/providers/aws/aws_regions_by_service.json" + # JSON objects regions_by_service = {} @@ -66,7 +67,38 @@ for item in original_matrix_regions_aws["prices"]: logging.info("Storing final JSON") regions_by_service["services"] = services +# Include the regions for the subservices and the services not present +logging.info("Updating subservices and the services not present in the original matrix") +# accessanalyzer --> iam +regions_by_service["services"]["accessanalyzer"] = regions_by_service["services"]["iam"] +# apigatewayv2 --> apigateway +regions_by_service["services"]["apigatewayv2"] = regions_by_service["services"][ + "apigateway" +] +# macie2 --> macie +regions_by_service["services"]["macie2"] = regions_by_service["services"]["macie"] +# logs --> cloudwatch +regions_by_service["services"]["logs"] = regions_by_service["services"]["cloudwatch"] +# dax --> dynamodb +regions_by_service["services"]["dax"] = regions_by_service["services"]["dynamodb"] +# glacier --> s3 +regions_by_service["services"]["glacier"] = regions_by_service["services"]["s3"] +# opensearch --> es +regions_by_service["services"]["opensearch"] = regions_by_service["services"]["es"] +# elbv2 --> elb +regions_by_service["services"]["elbv2"] = regions_by_service["services"]["elb"] +# route53domains --> route53 +regions_by_service["services"]["route53domains"] = regions_by_service["services"][ + "route53" +] +# s3control --> s3 +regions_by_service["services"]["s3control"] = regions_by_service["services"]["s3"] +# wafv2 --> waf +regions_by_service["services"]["wafv2"] = regions_by_service["services"]["waf"] +# waf-regional --> waf +regions_by_service["services"]["waf-regional"] = regions_by_service["services"]["waf"] + # Write to file -logging.info(f"Writing {parsed__matrix_regions_aws}") -with open(parsed__matrix_regions_aws, "w") as outfile: +logging.info(f"Writing {parsed_matrix_regions_aws}") +with open(parsed_matrix_regions_aws, "w") as outfile: json.dump(regions_by_service, outfile, indent=2, sort_keys=True)