diff --git a/prowler/providers/aws/services/autoscaling/autoscaling_find_secrets_ec2_launch_configuration/autoscaling_find_secrets_ec2_launch_configuration.metadata.json b/prowler/providers/aws/services/autoscaling/autoscaling_find_secrets_ec2_launch_configuration/autoscaling_find_secrets_ec2_launch_configuration.metadata.json index 93e99830..b2a61407 100644 --- a/prowler/providers/aws/services/autoscaling/autoscaling_find_secrets_ec2_launch_configuration/autoscaling_find_secrets_ec2_launch_configuration.metadata.json +++ b/prowler/providers/aws/services/autoscaling/autoscaling_find_secrets_ec2_launch_configuration/autoscaling_find_secrets_ec2_launch_configuration.metadata.json @@ -7,7 +7,7 @@ ], "ServiceName": "autoscaling", "SubServiceName": "", - "ResourceIdTemplate": "arn:partition:access-analyzer:region:account-id:analyzer/resource-id", + "ResourceIdTemplate": "arn:partition:autoscaling:region:account-id:autoScalingGroupName/resource-name", "Severity": "critical", "ResourceType": "Other", "Description": "Find secrets in EC2 Auto Scaling Launch Configuration", diff --git a/prowler/providers/aws/services/autoscaling/autoscaling_group_multiple_az/__init__.py b/prowler/providers/aws/services/autoscaling/autoscaling_group_multiple_az/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/prowler/providers/aws/services/autoscaling/autoscaling_group_multiple_az/autoscaling_group_multiple_az.metadata.json b/prowler/providers/aws/services/autoscaling/autoscaling_group_multiple_az/autoscaling_group_multiple_az.metadata.json new file mode 100644 index 00000000..cdd1b58f --- /dev/null +++ b/prowler/providers/aws/services/autoscaling/autoscaling_group_multiple_az/autoscaling_group_multiple_az.metadata.json @@ -0,0 +1,30 @@ +{ + "Provider": "aws", + "CheckID": "autoscaling_group_multiple_az", + "CheckTitle": "EC2 Auto Scaling Group should use multiple Availability Zones", + "CheckType": [], + "ServiceName": "autoscaling", + "SubServiceName": "", + "ResourceIdTemplate": "arn:partition:autoscaling:region:account-id:autoScalingGroupName/resource-name", + "Severity": "medium", + "ResourceType": "Other", + "Description": "EC2 Auto Scaling Group should use multiple Availability Zones", + "Risk": "In case of a failure in a single Availability Zone, the Auto Scaling Group will not be able to launch new instances to replace the failed ones.", + "RelatedUrl": "https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-add-availability-zone.html", + "Remediation": { + "Code": { + "CLI": "aws autoscaling update-auto-scaling-group", + "NativeIaC": "", + "Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/AutoScaling/multiple-availability-zones.html", + "Terraform": "" + }, + "Recommendation": { + "Text": "Configure multiple Availability Zones for EC2 Auto Scaling Group", + "Url": "https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-add-availability-zone.html" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/aws/services/autoscaling/autoscaling_group_multiple_az/autoscaling_group_multiple_az.py b/prowler/providers/aws/services/autoscaling/autoscaling_group_multiple_az/autoscaling_group_multiple_az.py new file mode 100644 index 00000000..5e96f608 --- /dev/null +++ b/prowler/providers/aws/services/autoscaling/autoscaling_group_multiple_az/autoscaling_group_multiple_az.py @@ -0,0 +1,28 @@ +from prowler.lib.check.models import Check, Check_Report_AWS +from prowler.providers.aws.services.autoscaling.autoscaling_client import ( + autoscaling_client, +) + + +class autoscaling_group_multiple_az(Check): + def execute(self): + findings = [] + for group in autoscaling_client.groups: + report = Check_Report_AWS(self.metadata()) + report.region = group.region + report.resource_id = group.name + report.resource_arn = group.arn + report.resource_tags = group.tags + report.status = "FAIL" + report.status_extended = ( + f"Autoscaling group {group.name} has only one availability zones." + ) + if len(group.availability_zones) > 1: + report.status = "PASS" + report.status_extended = ( + f"Autoscaling group {group.name} has multiple availability zones." + ) + + findings.append(report) + + return findings diff --git a/prowler/providers/aws/services/autoscaling/autoscaling_service.py b/prowler/providers/aws/services/autoscaling/autoscaling_service.py index 51ebdfb5..c2de0536 100644 --- a/prowler/providers/aws/services/autoscaling/autoscaling_service.py +++ b/prowler/providers/aws/services/autoscaling/autoscaling_service.py @@ -17,6 +17,8 @@ class AutoScaling: self.regional_clients = generate_regional_clients(self.service, audit_info) self.launch_configurations = [] self.__threading_call__(self.__describe_launch_configurations__) + self.groups = [] + self.__threading_call__(self.__describe_auto_scaling_groups__) def __get_session__(self): return self.session @@ -59,6 +61,35 @@ class AutoScaling: f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" ) + def __describe_auto_scaling_groups__(self, regional_client): + logger.info("AutoScaling - Describing AutoScaling Groups...") + try: + describe_auto_scaling_groups_paginator = regional_client.get_paginator( + "describe_auto_scaling_groups" + ) + for page in describe_auto_scaling_groups_paginator.paginate(): + for group in page["AutoScalingGroups"]: + if not self.audit_resources or ( + is_resource_filtered( + group["AutoScalingGroupARN"], + self.audit_resources, + ) + ): + self.groups.append( + Group( + arn=group.get("AutoScalingGroupARN"), + name=group.get("AutoScalingGroupName"), + region=regional_client.region, + availability_zones=group.get("AvailabilityZones"), + tags=group.get("Tags"), + ) + ) + + except Exception as error: + logger.error( + f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + class LaunchConfiguration(BaseModel): arn: str @@ -66,3 +97,11 @@ class LaunchConfiguration(BaseModel): user_data: str image_id: str region: str + + +class Group(BaseModel): + arn: str + name: str + region: str + availability_zones: list + tags: list = [] diff --git a/tests/providers/aws/services/autoscaling/autoscaling_group_multiple_az/autoscaling_group_multiple_az_test.py b/tests/providers/aws/services/autoscaling/autoscaling_group_multiple_az/autoscaling_group_multiple_az_test.py new file mode 100644 index 00000000..559e478e --- /dev/null +++ b/tests/providers/aws/services/autoscaling/autoscaling_group_multiple_az/autoscaling_group_multiple_az_test.py @@ -0,0 +1,225 @@ +from unittest import mock + +from boto3 import client, session +from moto import mock_autoscaling + +from prowler.providers.aws.lib.audit_info.models import AWS_Audit_Info + +AWS_REGION = "us-east-1" +AWS_ACCOUNT_NUMBER = "123456789012" + + +class Test_autoscaling_group_multiple_az: + def set_mocked_audit_info(self): + audit_info = AWS_Audit_Info( + session_config=None, + original_session=None, + audit_session=session.Session( + profile_name=None, + botocore_session=None, + ), + audited_account=AWS_ACCOUNT_NUMBER, + audited_user_id=None, + audited_partition="aws", + audited_identity_arn=None, + profile=None, + profile_region=None, + credentials=None, + assumed_role_info=None, + audited_regions=["us-east-1", "eu-west-1"], + organizations_metadata=None, + audit_resources=None, + ) + + return audit_info + + @mock_autoscaling + def test_no_autoscaling(self): + autoscaling_client = client("autoscaling", region_name=AWS_REGION) + autoscaling_client.groups = [] + + from prowler.providers.aws.services.autoscaling.autoscaling_service import ( + AutoScaling, + ) + + current_audit_info = self.set_mocked_audit_info() + + with mock.patch( + "prowler.providers.aws.lib.audit_info.audit_info.current_audit_info", + new=current_audit_info, + ), mock.patch( + "prowler.providers.aws.services.autoscaling.autoscaling_group_multiple_az.autoscaling_group_multiple_az.autoscaling_client", + new=AutoScaling(current_audit_info), + ): + # Test Check + from prowler.providers.aws.services.autoscaling.autoscaling_group_multiple_az.autoscaling_group_multiple_az import ( + autoscaling_group_multiple_az, + ) + + check = autoscaling_group_multiple_az() + result = check.execute() + + assert len(result) == 0 + + @mock_autoscaling + def test_groups_with_multi_az(self): + autoscaling_client = client("autoscaling", region_name=AWS_REGION) + autoscaling_client.create_launch_configuration( + LaunchConfigurationName="test", + ImageId="ami-12c6146b", + InstanceType="t1.micro", + KeyName="the_keys", + SecurityGroups=["default", "default2"], + ) + autoscaling_client.create_auto_scaling_group( + AutoScalingGroupName="my-autoscaling-group", + LaunchConfigurationName="test", + MinSize=0, + MaxSize=0, + DesiredCapacity=0, + AvailabilityZones=["us-east-1a", "us-east-1b"], + ) + + from prowler.providers.aws.services.autoscaling.autoscaling_service import ( + AutoScaling, + ) + + current_audit_info = self.set_mocked_audit_info() + + with mock.patch( + "prowler.providers.aws.lib.audit_info.audit_info.current_audit_info", + new=current_audit_info, + ), mock.patch( + "prowler.providers.aws.services.autoscaling.autoscaling_group_multiple_az.autoscaling_group_multiple_az.autoscaling_client", + new=AutoScaling(current_audit_info), + ): + # Test Check + from prowler.providers.aws.services.autoscaling.autoscaling_group_multiple_az.autoscaling_group_multiple_az import ( + autoscaling_group_multiple_az, + ) + + check = autoscaling_group_multiple_az() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "Autoscaling group my-autoscaling-group has multiple availability zones." + ) + assert result[0].resource_id == "my-autoscaling-group" + assert result[0].resource_tags == [] + + @mock_autoscaling + def test_groups_with_single_az(self): + autoscaling_client = client("autoscaling", region_name=AWS_REGION) + autoscaling_client.create_launch_configuration( + LaunchConfigurationName="test", + ImageId="ami-12c6146b", + InstanceType="t1.micro", + KeyName="the_keys", + SecurityGroups=["default", "default2"], + ) + autoscaling_client.create_auto_scaling_group( + AutoScalingGroupName="my-autoscaling-group", + LaunchConfigurationName="test", + MinSize=0, + MaxSize=0, + DesiredCapacity=0, + AvailabilityZones=["us-east-1a"], + ) + + from prowler.providers.aws.services.autoscaling.autoscaling_service import ( + AutoScaling, + ) + + current_audit_info = self.set_mocked_audit_info() + + with mock.patch( + "prowler.providers.aws.lib.audit_info.audit_info.current_audit_info", + new=current_audit_info, + ), mock.patch( + "prowler.providers.aws.services.autoscaling.autoscaling_group_multiple_az.autoscaling_group_multiple_az.autoscaling_client", + new=AutoScaling(current_audit_info), + ): + # Test Check + from prowler.providers.aws.services.autoscaling.autoscaling_group_multiple_az.autoscaling_group_multiple_az import ( + autoscaling_group_multiple_az, + ) + + check = autoscaling_group_multiple_az() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "Autoscaling group my-autoscaling-group has only one availability zones." + ) + assert result[0].resource_id == "my-autoscaling-group" + assert result[0].resource_tags == [] + + @mock_autoscaling + def test_groups_witd_and_without(self): + autoscaling_client = client("autoscaling", region_name=AWS_REGION) + autoscaling_client.create_launch_configuration( + LaunchConfigurationName="test", + ImageId="ami-12c6146b", + InstanceType="t1.micro", + KeyName="the_keys", + SecurityGroups=["default", "default2"], + ) + autoscaling_client.create_auto_scaling_group( + AutoScalingGroupName="asg-multiple", + LaunchConfigurationName="test", + MinSize=0, + MaxSize=0, + DesiredCapacity=0, + AvailabilityZones=["us-east-1a", "us-east-1b"], + ) + autoscaling_client.create_auto_scaling_group( + AutoScalingGroupName="asg-single", + LaunchConfigurationName="test", + MinSize=0, + MaxSize=0, + DesiredCapacity=0, + AvailabilityZones=["us-east-1a"], + ) + + from prowler.providers.aws.services.autoscaling.autoscaling_service import ( + AutoScaling, + ) + + current_audit_info = self.set_mocked_audit_info() + + with mock.patch( + "prowler.providers.aws.lib.audit_info.audit_info.current_audit_info", + new=current_audit_info, + ), mock.patch( + "prowler.providers.aws.services.autoscaling.autoscaling_group_multiple_az.autoscaling_group_multiple_az.autoscaling_client", + new=AutoScaling(current_audit_info), + ): + # Test Check + from prowler.providers.aws.services.autoscaling.autoscaling_group_multiple_az.autoscaling_group_multiple_az import ( + autoscaling_group_multiple_az, + ) + + check = autoscaling_group_multiple_az() + result = check.execute() + + assert len(result) == 2 + for check in result: + if check.resource_id == "asg-multiple": + assert check.status == "PASS" + assert ( + check.status_extended + == "Autoscaling group asg-multiple has multiple availability zones." + ) + assert check.resource_tags == [] + if check.resource_id == "asg-single": + assert check.status == "FAIL" + assert ( + check.status_extended + == "Autoscaling group asg-single has only one availability zones." + ) + assert check.resource_tags == [] diff --git a/tests/providers/aws/services/autoscaling/autoscaling_service_test.py b/tests/providers/aws/services/autoscaling/autoscaling_service_test.py index 4a2cc145..cb042d0e 100644 --- a/tests/providers/aws/services/autoscaling/autoscaling_service_test.py +++ b/tests/providers/aws/services/autoscaling/autoscaling_service_test.py @@ -100,3 +100,50 @@ class Test_AutoScaling_Service: assert autoscaling.launch_configurations[0].image_id == "ami-12c6146b" assert autoscaling.launch_configurations[1].image_id == "ami-12c6146b" assert autoscaling.launch_configurations[1].name == "tester2" + + # Test Describe Auto Scaling Groups + @mock_autoscaling + def test__describe_auto_scaling_groups__(self): + # Generate AutoScaling Client + autoscaling_client = client("autoscaling", region_name=AWS_REGION) + autoscaling_client.create_launch_configuration( + LaunchConfigurationName="test", + ImageId="ami-12c6146b", + InstanceType="t1.micro", + KeyName="the_keys", + SecurityGroups=["default", "default2"], + ) + asg = autoscaling_client.create_auto_scaling_group( + AutoScalingGroupName="my-autoscaling-group", + LaunchConfigurationName="test", + MinSize=0, + MaxSize=0, + DesiredCapacity=0, + AvailabilityZones=["us-east-1a", "us-east-1b"], + Tags=[ + { + "Key": "tag_test", + "Value": "value_test", + }, + ], + ) + + # AutoScaling client for this test class + audit_info = self.set_mocked_audit_info() + autoscaling = AutoScaling(audit_info) + print("asg", asg) + assert len(autoscaling.groups) == 1 + # create_auto_scaling_group doesn't return the ARN, can't check it + # assert autoscaling.groups[0].arn == + assert autoscaling.groups[0].name == "my-autoscaling-group" + assert autoscaling.groups[0].region == AWS_REGION + assert autoscaling.groups[0].availability_zones == ["us-east-1a", "us-east-1b"] + assert autoscaling.groups[0].tags == [ + { + "Key": "tag_test", + "PropagateAtLaunch": False, + "ResourceId": "my-autoscaling-group", + "ResourceType": "auto-scaling-group", + "Value": "value_test", + } + ]