diff --git a/docs/tutorials/pentesting.md b/docs/tutorials/pentesting.md index b60f76b0df..2bdb8ebd9a 100644 --- a/docs/tutorials/pentesting.md +++ b/docs/tutorials/pentesting.md @@ -8,11 +8,12 @@ Prowler uses `detect-secrets` library to search for any secrets that are stores The actual checks that have this functionality are: -1. autoscaling_find_secrets_ec2_launch_configuration +- autoscaling_find_secrets_ec2_launch_configuration - awslambda_function_no_secrets_in_code - awslambda_function_no_secrets_in_variables - cloudformation_stack_outputs_find_secrets - ec2_instance_secrets_user_data +- ec2_launch_template_no_secrets - ecs_task_definitions_no_environment_secrets - ssm_document_secrets diff --git a/prowler/compliance/aws/aws_well_architected_framework_security_pillar_aws.json b/prowler/compliance/aws/aws_well_architected_framework_security_pillar_aws.json index bc5d9902e1..9dd4d1ccbd 100644 --- a/prowler/compliance/aws/aws_well_architected_framework_security_pillar_aws.json +++ b/prowler/compliance/aws/aws_well_architected_framework_security_pillar_aws.json @@ -236,6 +236,7 @@ "awslambda_function_no_secrets_in_variables", "cloudformation_stack_outputs_find_secrets", "ec2_instance_secrets_user_data", + "ec2_launch_template_no_secrets", "ecs_task_definitions_no_environment_secrets", "ssm_document_secrets" ] diff --git a/prowler/compliance/aws/mitre_attack_aws.json b/prowler/compliance/aws/mitre_attack_aws.json index 1827650305..04feda7d97 100644 --- a/prowler/compliance/aws/mitre_attack_aws.json +++ b/prowler/compliance/aws/mitre_attack_aws.json @@ -947,6 +947,7 @@ "cloudformation_stack_outputs_find_secrets", "cloudwatch_log_group_no_secrets_in_logs", "ec2_instance_secrets_user_data", + "ec2_launch_template_no_secrets", "ecs_task_definitions_no_environment_secrets", "eks_cluster_kms_cmk_encryption_in_secrets_enabled", "ssm_document_secrets", diff --git a/prowler/providers/aws/services/ec2/ec2_launch_template_no_secrets/__init__.py b/prowler/providers/aws/services/ec2/ec2_launch_template_no_secrets/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/aws/services/ec2/ec2_launch_template_no_secrets/ec2_launch_template_no_secrets.metadata.json b/prowler/providers/aws/services/ec2/ec2_launch_template_no_secrets/ec2_launch_template_no_secrets.metadata.json new file mode 100644 index 0000000000..3338cf3eaf --- /dev/null +++ b/prowler/providers/aws/services/ec2/ec2_launch_template_no_secrets/ec2_launch_template_no_secrets.metadata.json @@ -0,0 +1,32 @@ +{ + "Provider": "aws", + "CheckID": "ec2_launch_template_no_secrets", + "CheckTitle": "Find secrets in EC2 Launch Template", + "CheckType": [], + "ServiceName": "ec2", + "SubServiceName": "", + "ResourceIdTemplate": "arn:partition:ec2:region:account-id:launch-template/template-id", + "Severity": "critical", + "ResourceType": "AwsEc2LaunchTemplate", + "Description": "Find secrets in EC2 Launch Template", + "Risk": "The use of a hard-coded password increases the possibility of password guessing. If hard-coded passwords are used, it is possible that malicious users gain access through the account in question.", + "RelatedUrl": "https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-launch-templates.html", + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "Do not include sensitive information in user data within the launch templates, try to use Secrets Manager instead.", + "Url": "https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.html" + } + }, + "Categories": [ + "secrets" + ], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/aws/services/ec2/ec2_launch_template_no_secrets/ec2_launch_template_no_secrets.py b/prowler/providers/aws/services/ec2/ec2_launch_template_no_secrets/ec2_launch_template_no_secrets.py new file mode 100644 index 0000000000..f04b47dc7a --- /dev/null +++ b/prowler/providers/aws/services/ec2/ec2_launch_template_no_secrets/ec2_launch_template_no_secrets.py @@ -0,0 +1,60 @@ +import os +import tempfile +import zlib +from base64 import b64decode + +from detect_secrets import SecretsCollection +from detect_secrets.settings import default_settings + +from prowler.lib.check.models import Check, Check_Report_AWS +from prowler.providers.aws.services.ec2.ec2_client import ec2_client + + +class ec2_launch_template_no_secrets(Check): + def execute(self): + findings = [] + for template in ec2_client.launch_templates: + report = Check_Report_AWS(self.metadata()) + report.region = template.region + report.resource_id = template.id + report.resource_arn = template.arn + + versions_with_secrets = [] + + for version in template.versions: + if "UserData" not in version.template_data: + continue + + temp_user_data_file = tempfile.NamedTemporaryFile(delete=False) + user_data = b64decode(version.template_data["UserData"]) + + if user_data[0:2] == b"\x1f\x8b": # GZIP magic number + user_data = zlib.decompress(user_data, zlib.MAX_WBITS | 32).decode( + "utf-8" + ) + else: + user_data = user_data.decode("utf-8") + + temp_user_data_file.write( + bytes(user_data, encoding="raw_unicode_escape") + ) + temp_user_data_file.close() + secrets = SecretsCollection() + with default_settings(): + secrets.scan_file(temp_user_data_file.name) + + if secrets.json(): + versions_with_secrets.append(str(version.version_number)) + + os.remove(temp_user_data_file.name) + + if len(versions_with_secrets) > 0: + report.status = "FAIL" + report.status_extended = f"Potential secret found in User Data for EC2 Launch Template {template.name} in template versions: {', '.join(versions_with_secrets)}." + else: + report.status = "PASS" + report.status_extended = f"No secrets found in User Data of any version for EC2 Launch Template {template.name}." + + findings.append(report) + + return findings diff --git a/prowler/providers/aws/services/ec2/ec2_service.py b/prowler/providers/aws/services/ec2/ec2_service.py index e7321a96d9..ac0c9e07ca 100644 --- a/prowler/providers/aws/services/ec2/ec2_service.py +++ b/prowler/providers/aws/services/ec2/ec2_service.py @@ -44,6 +44,11 @@ class EC2(AWSService): self.__threading_call__(self.__get_snapshot_block_public_access_state__) self.instance_metadata_defaults = [] self.__threading_call__(self.__get_instance_metadata_defaults__) + self.launch_templates = [] + self.__threading_call__(self.__describe_launch_templates) + self.__threading_call__( + self.__get_launch_template_versions__, self.launch_templates + ) def __get_volume_arn_template__(self, region): return ( @@ -470,6 +475,56 @@ class EC2(AWSService): f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" ) + def __describe_launch_templates(self, regional_client): + try: + describe_launch_templates_paginator = regional_client.get_paginator( + "describe_launch_templates" + ) + + for page in describe_launch_templates_paginator.paginate(): + for template in page["LaunchTemplates"]: + template_arn = f"arn:aws:ec2:{regional_client.region}:{self.audited_account}:launch-template/{template['LaunchTemplateId']}" + if not self.audit_resources or ( + is_resource_filtered(template_arn, self.audit_resources) + ): + self.launch_templates.append( + LaunchTemplate( + name=template["LaunchTemplateName"], + id=template["LaunchTemplateId"], + arn=template_arn, + region=regional_client.region, + versions=[], + ) + ) + + except Exception as error: + logger.error( + f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + + def __get_launch_template_versions__(self, launch_template): + try: + regional_client = self.regional_clients[launch_template.region] + describe_launch_template_versions_paginator = regional_client.get_paginator( + "describe_launch_template_versions" + ) + + for page in describe_launch_template_versions_paginator.paginate( + LaunchTemplateId=launch_template.id + ): + for template_version in page["LaunchTemplateVersions"]: + launch_template.versions.append( + LaunchTemplateVersion( + version_number=template_version["VersionNumber"], + template_data=template_version["LaunchTemplateData"], + ) + ) + + except Exception as error: + logger.error( + f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + class Instance(BaseModel): id: str @@ -577,3 +632,16 @@ class InstanceMetadataDefaults(BaseModel): http_tokens: Optional[str] instances: bool region: str + + +class LaunchTemplateVersion(BaseModel): + version_number: int + template_data: dict + + +class LaunchTemplate(BaseModel): + name: str + id: str + arn: str + region: str + versions: list[LaunchTemplateVersion] = [] diff --git a/tests/providers/aws/services/ec2/ec2_launch_template_no_secrets/ec2_launch_template_no_secrets_test.py b/tests/providers/aws/services/ec2/ec2_launch_template_no_secrets/ec2_launch_template_no_secrets_test.py new file mode 100644 index 0000000000..e085e94c93 --- /dev/null +++ b/tests/providers/aws/services/ec2/ec2_launch_template_no_secrets/ec2_launch_template_no_secrets_test.py @@ -0,0 +1,445 @@ +from base64 import b64encode +from os import path +from pathlib import Path +from unittest import mock + +from boto3 import client +from moto import mock_aws + +from tests.providers.aws.utils import AWS_REGION_US_EAST_1, set_mocked_aws_provider + +ACTUAL_DIRECTORY = Path(path.dirname(path.realpath(__file__))) +FIXTURES_DIR_NAME = "fixtures" + + +class Test_ec2_launch_template_no_secrets: + @mock_aws + def test_no_launch_templates(self): + ec2_client = client("ec2", region_name=AWS_REGION_US_EAST_1) + ec2_client.launch_templates = [] + + from prowler.providers.aws.services.ec2.ec2_service import EC2 + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.ec2.ec2_launch_template_no_secrets.ec2_launch_template_no_secrets.ec2_client", + new=EC2(aws_provider), + ): + # Test Check + from prowler.providers.aws.services.ec2.ec2_launch_template_no_secrets.ec2_launch_template_no_secrets import ( + ec2_launch_template_no_secrets, + ) + + check = ec2_launch_template_no_secrets() + result = check.execute() + + assert len(result) == 0 + + @mock_aws + def test_one_launch_template_with_no_secrets(self): + # Include launch_template to check + launch_template_name = "tester" + ec2_client = client("ec2", region_name=AWS_REGION_US_EAST_1) + ec2_client.create_launch_template( + LaunchTemplateName=launch_template_name, + VersionDescription="Launch Template without secrets", + LaunchTemplateData={ + "InstanceType": "t1.micro", + "UserData": b64encode("This is some user_data".encode("utf-8")).decode( + "utf-8" + ), + }, + ) + + launch_template_id = ec2_client.describe_launch_templates()["LaunchTemplates"][ + 0 + ]["LaunchTemplateId"] + + from prowler.providers.aws.services.ec2.ec2_service import EC2 + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.ec2.ec2_launch_template_no_secrets.ec2_launch_template_no_secrets.ec2_client", + new=EC2(aws_provider), + ): + # Test Check + from prowler.providers.aws.services.ec2.ec2_launch_template_no_secrets.ec2_launch_template_no_secrets import ( + ec2_launch_template_no_secrets, + ) + + check = ec2_launch_template_no_secrets() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"No secrets found in User Data of any version for EC2 Launch Template {launch_template_name}." + ) + assert result[0].resource_id == launch_template_id + assert result[0].region == AWS_REGION_US_EAST_1 + + @mock_aws + def test_one_launch_template_with_secrets(self): + launch_template_name = "tester" + + f = open( + f"{ACTUAL_DIRECTORY}/{FIXTURES_DIR_NAME}/fixture", + "rb", + ) + secrets = f.read() + + ec2_client = client("ec2", region_name=AWS_REGION_US_EAST_1) + ec2_client.create_launch_template( + LaunchTemplateName=launch_template_name, + VersionDescription="Launch Template with secrets", + LaunchTemplateData={ + "InstanceType": "t1.micro", + "UserData": b64encode(secrets).decode("utf-8"), + }, + ) + + launch_template_version = ec2_client.describe_launch_template_versions( + LaunchTemplateName=launch_template_name + )["LaunchTemplateVersions"][0]["VersionNumber"] + + launch_template_id = ec2_client.describe_launch_templates()["LaunchTemplates"][ + 0 + ]["LaunchTemplateId"] + + from prowler.providers.aws.services.ec2.ec2_service import EC2 + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.ec2.ec2_launch_template_no_secrets.ec2_launch_template_no_secrets.ec2_client", + new=EC2(aws_provider), + ): + # Test Check + from prowler.providers.aws.services.ec2.ec2_launch_template_no_secrets.ec2_launch_template_no_secrets import ( + ec2_launch_template_no_secrets, + ) + + check = ec2_launch_template_no_secrets() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Potential secret found in User Data for EC2 Launch Template {launch_template_name} in template versions: {launch_template_version}." + ) + assert result[0].resource_id == launch_template_id + assert result[0].region == AWS_REGION_US_EAST_1 + + @mock_aws + def test_one_launch_template_with_secrets_in_multiple_versions(self): + launch_template_name = "tester" + + f = open( + f"{ACTUAL_DIRECTORY}/{FIXTURES_DIR_NAME}/fixture", + "rb", + ) + secrets = f.read() + + ec2_client = client("ec2", region_name=AWS_REGION_US_EAST_1) + ec2_client.create_launch_template( + LaunchTemplateName=launch_template_name, + VersionDescription="Launch Template with secrets", + LaunchTemplateData={ + "InstanceType": "t1.micro", + "UserData": b64encode(secrets).decode("utf-8"), + }, + ) + + ec2_client.create_launch_template_version( + LaunchTemplateName=launch_template_name, + VersionDescription="Second Launch Template version with secrets", + LaunchTemplateData={ + "InstanceType": "t1.micro", + "UserData": b64encode(secrets).decode("utf-8"), + }, + ) + + launch_template_id = ec2_client.describe_launch_templates()["LaunchTemplates"][ + 0 + ]["LaunchTemplateId"] + + launch_template_version_numbers = [ + str(v["VersionNumber"]) + for v in ec2_client.describe_launch_template_versions( + LaunchTemplateName=launch_template_name + )["LaunchTemplateVersions"] + ] + + from prowler.providers.aws.services.ec2.ec2_service import EC2 + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.ec2.ec2_launch_template_no_secrets.ec2_launch_template_no_secrets.ec2_client", + new=EC2(aws_provider), + ): + # Test Check + from prowler.providers.aws.services.ec2.ec2_launch_template_no_secrets.ec2_launch_template_no_secrets import ( + ec2_launch_template_no_secrets, + ) + + check = ec2_launch_template_no_secrets() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Potential secret found in User Data for EC2 Launch Template {launch_template_name} in template versions: {', '.join(launch_template_version_numbers)}." + ) + assert result[0].resource_id == launch_template_id + assert result[0].region == AWS_REGION_US_EAST_1 + + @mock_aws + def test_one_launch_template_with_secrets_in_single_version(self): + launch_template_name = "tester" + + f = open( + f"{ACTUAL_DIRECTORY}/{FIXTURES_DIR_NAME}/fixture", + "rb", + ) + secrets = f.read() + + ec2_client = client("ec2", region_name=AWS_REGION_US_EAST_1) + ec2_client.create_launch_template( + LaunchTemplateName=launch_template_name, + VersionDescription="Launch Template with secrets", + LaunchTemplateData={ + "InstanceType": "t1.micro", + "UserData": b64encode(secrets).decode("utf-8"), + }, + ) + + version_with_secrets = ec2_client.describe_launch_template_versions( + LaunchTemplateName=launch_template_name, + )["LaunchTemplateVersions"][0]["VersionNumber"] + + ec2_client.create_launch_template_version( + LaunchTemplateName=launch_template_name, + VersionDescription="Second Launch Template version without secrets", + LaunchTemplateData={ + "InstanceType": "t1.micro", + }, + ) + + launch_template_id = ec2_client.describe_launch_templates()["LaunchTemplates"][ + 0 + ]["LaunchTemplateId"] + + from prowler.providers.aws.services.ec2.ec2_service import EC2 + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.ec2.ec2_launch_template_no_secrets.ec2_launch_template_no_secrets.ec2_client", + new=EC2(aws_provider), + ): + # Test Check + from prowler.providers.aws.services.ec2.ec2_launch_template_no_secrets.ec2_launch_template_no_secrets import ( + ec2_launch_template_no_secrets, + ) + + check = ec2_launch_template_no_secrets() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Potential secret found in User Data for EC2 Launch Template {launch_template_name} in template versions: {version_with_secrets}." + ) + assert result[0].resource_id == launch_template_id + assert result[0].region == AWS_REGION_US_EAST_1 + + @mock_aws + def test_one_launch_template_with_secrets_gzip(self): + launch_template_name = "tester" + + f = open( + f"{ACTUAL_DIRECTORY}/{FIXTURES_DIR_NAME}/fixture.gz", + "rb", + ) + secrets = f.read() + + ec2_client = client("ec2", region_name=AWS_REGION_US_EAST_1) + ec2_client.create_launch_template( + LaunchTemplateName=launch_template_name, + VersionDescription="Launch Template with secrets", + LaunchTemplateData={ + "InstanceType": "t1.micro", + "UserData": b64encode(secrets).decode("utf-8"), + }, + ) + + launch_template_version = ec2_client.describe_launch_template_versions( + LaunchTemplateName=launch_template_name + )["LaunchTemplateVersions"][0]["VersionNumber"] + + launch_template_id = ec2_client.describe_launch_templates()["LaunchTemplates"][ + 0 + ]["LaunchTemplateId"] + + from prowler.providers.aws.services.ec2.ec2_service import EC2 + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.ec2.ec2_launch_template_no_secrets.ec2_launch_template_no_secrets.ec2_client", + new=EC2(aws_provider), + ): + # Test Check + from prowler.providers.aws.services.ec2.ec2_launch_template_no_secrets.ec2_launch_template_no_secrets import ( + ec2_launch_template_no_secrets, + ) + + check = ec2_launch_template_no_secrets() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Potential secret found in User Data for EC2 Launch Template {launch_template_name} in template versions: {launch_template_version}." + ) + assert result[0].resource_id == launch_template_id + assert result[0].region == AWS_REGION_US_EAST_1 + + @mock_aws + def test_one_launch_template_without_user_data(self): + launch_template_name = "tester" + + ec2_client = client("ec2", region_name=AWS_REGION_US_EAST_1) + ec2_client.create_launch_template( + LaunchTemplateName=launch_template_name, + VersionDescription="Launch Template without user data", + LaunchTemplateData={ + "InstanceType": "t1.micro", + }, + ) + + launch_template_id = ec2_client.describe_launch_templates()["LaunchTemplates"][ + 0 + ]["LaunchTemplateId"] + + from prowler.providers.aws.services.ec2.ec2_service import EC2 + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.ec2.ec2_launch_template_no_secrets.ec2_launch_template_no_secrets.ec2_client", + new=EC2(aws_provider), + ): + # Test Check + from prowler.providers.aws.services.ec2.ec2_launch_template_no_secrets.ec2_launch_template_no_secrets import ( + ec2_launch_template_no_secrets, + ) + + check = ec2_launch_template_no_secrets() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"No secrets found in User Data of any version for EC2 Launch Template {launch_template_name}." + ) + assert result[0].resource_id == launch_template_id + assert result[0].region == AWS_REGION_US_EAST_1 + + @mock_aws + def test_two_launch_templates_one_template_with_secrets(self): + launch_template_name_with_secrets = "tester1" + launch_template_name_without_secrets = "tester2" + + f = open( + f"{ACTUAL_DIRECTORY}/{FIXTURES_DIR_NAME}/fixture", + "rb", + ) + secrets = f.read() + + ec2_client = client("ec2", region_name=AWS_REGION_US_EAST_1) + ec2_client.create_launch_template( + LaunchTemplateName=launch_template_name_with_secrets, + VersionDescription="Launch Template with secrets", + LaunchTemplateData={ + "InstanceType": "t1.micro", + "UserData": b64encode(secrets).decode("utf-8"), + }, + ) + + launch_template_id = ec2_client.describe_launch_templates()["LaunchTemplates"][ + 0 + ]["LaunchTemplateId"] + template_version_with_secrets = ec2_client.describe_launch_template_versions( + LaunchTemplateName=launch_template_name_with_secrets + )["LaunchTemplateVersions"][0]["VersionNumber"] + + # Create second launch template with no secret in UserData (NOT a new version) + ec2_client.create_launch_template( + LaunchTemplateName=launch_template_name_without_secrets, + VersionDescription="Launch Template without secrets", + LaunchTemplateData={ + "InstanceType": "t1.micro", + "UserData": b64encode(b"Test").decode("utf-8"), + }, + ) + + from prowler.providers.aws.services.ec2.ec2_service import EC2 + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.ec2.ec2_launch_template_no_secrets.ec2_launch_template_no_secrets.ec2_client", + new=EC2(aws_provider), + ): + # Test Check + from prowler.providers.aws.services.ec2.ec2_launch_template_no_secrets.ec2_launch_template_no_secrets import ( + ec2_launch_template_no_secrets, + ) + + check = ec2_launch_template_no_secrets() + result = check.execute() + + assert len(result) == 2 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Potential secret found in User Data for EC2 Launch Template {launch_template_name_with_secrets} in template versions: {template_version_with_secrets}." + ) + assert result[0].resource_id == launch_template_id + assert result[0].region == AWS_REGION_US_EAST_1 + + assert result[1].status == "PASS" diff --git a/tests/providers/aws/services/ec2/ec2_launch_template_no_secrets/fixtures/fixture b/tests/providers/aws/services/ec2/ec2_launch_template_no_secrets/fixtures/fixture new file mode 100644 index 0000000000..2fb5138932 --- /dev/null +++ b/tests/providers/aws/services/ec2/ec2_launch_template_no_secrets/fixtures/fixture @@ -0,0 +1,4 @@ +DB_PASSWORD=foobar123 +DB_USER=foo +API_KEY=12345abcd +SERVICE_PASSWORD=bbaabb45 diff --git a/tests/providers/aws/services/ec2/ec2_launch_template_no_secrets/fixtures/fixture.gz b/tests/providers/aws/services/ec2/ec2_launch_template_no_secrets/fixtures/fixture.gz new file mode 100644 index 0000000000..6120fcfbc4 Binary files /dev/null and b/tests/providers/aws/services/ec2/ec2_launch_template_no_secrets/fixtures/fixture.gz differ diff --git a/tests/providers/aws/services/ec2/ec2_service_test.py b/tests/providers/aws/services/ec2/ec2_service_test.py index f56640d45a..450c020247 100644 --- a/tests/providers/aws/services/ec2/ec2_service_test.py +++ b/tests/providers/aws/services/ec2/ec2_service_test.py @@ -1,6 +1,6 @@ import ipaddress import re -from base64 import b64decode +from base64 import b64decode, b64encode from datetime import datetime import botocore @@ -615,3 +615,92 @@ class Test_EC2_Service: assert ec2.volumes[0].tags == [ {"Key": "test", "Value": "test"}, ] + + # Test EC2 Describe Launch Templates + @mock_aws + def test__describe_launch_templates__(self): + # Generate EC2 Client + ec2_client = client("ec2", region_name=AWS_REGION_US_EAST_1) + + TEMPLATE_NAME = "tester1" + TEMPLATE_INSTANCE_TYPE = "c5.large" + KNOWN_SECRET_USER_DATA = "DB_PASSWORD=foobar123" + + # Create EC2 Launch Template API + ec2_client.create_launch_template( + LaunchTemplateName=TEMPLATE_NAME, + VersionDescription="Test EC Launch Template 1 (Secret in UserData)", + LaunchTemplateData={ + "InstanceType": TEMPLATE_INSTANCE_TYPE, + "UserData": b64encode(KNOWN_SECRET_USER_DATA.encode("utf-8")).decode( + "utf-8" + ), + }, + ) + + # EC2 client for this test class + aws_provider = set_mocked_aws_provider( + [AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1] + ) + ec2 = EC2(aws_provider) + + assert len(ec2.launch_templates) == 1 + assert ec2.launch_templates[0].name == TEMPLATE_NAME + assert ec2.launch_templates[0].region == AWS_REGION_US_EAST_1 + assert ( + ec2.launch_templates[0].arn + == f"arn:aws:ec2:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:launch-template/{ec2.launch_templates[0].id}" + ) + + # Test EC2 Describe Launch Templates + @mock_aws + def test__get_launch_template_versions__(self): + # Generate EC2 Client + ec2_client = client("ec2", region_name=AWS_REGION_US_EAST_1) + + TEMPLATE_NAME = "tester1" + TEMPLATE_INSTANCE_TYPE = "c5.large" + KNOWN_SECRET_USER_DATA = "DB_PASSWORD=foobar123" + + # Create EC2 Launch Template API + ec2_client.create_launch_template( + LaunchTemplateName=TEMPLATE_NAME, + VersionDescription="Test EC Launch Template 1", + LaunchTemplateData={ + "InstanceType": TEMPLATE_INSTANCE_TYPE, + }, + ) + + # Create EC2 Launch Template Version API + ec2_client.create_launch_template_version( + LaunchTemplateName=TEMPLATE_NAME, + VersionDescription="Updated Test EC Launch Template 1", + LaunchTemplateData={ + "InstanceType": TEMPLATE_INSTANCE_TYPE, + "UserData": b64encode(KNOWN_SECRET_USER_DATA.encode("utf-8")).decode( + "utf-8" + ), + }, + ) + + # EC2 client for this test class + aws_provider = set_mocked_aws_provider( + [AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1] + ) + ec2 = EC2(aws_provider) + + assert len(ec2.launch_templates) == 1 + assert ec2.launch_templates[0].name == TEMPLATE_NAME + assert ec2.launch_templates[0].region == AWS_REGION_US_EAST_1 + + assert len(ec2.launch_templates[0].versions) == 2 + + version1, version2 = ec2.launch_templates[0].versions + + assert version1.template_data["InstanceType"] == TEMPLATE_INSTANCE_TYPE + + assert version2.template_data["InstanceType"] == TEMPLATE_INSTANCE_TYPE + assert ( + b64decode(version2.template_data["UserData"]).decode("utf-8") + == KNOWN_SECRET_USER_DATA + )