From ec03ea5bc165c78a65b087515c2cffc91b3a0ca2 Mon Sep 17 00:00:00 2001 From: Gabriel Soltz Date: Wed, 10 May 2023 15:40:42 +0200 Subject: [PATCH] feat(workspaces): New check workspaces_vpc_2private_1public_subnets_nat (#2286) Co-authored-by: Pepe Fagoaga Co-authored-by: n4ch04 --- .../providers/aws/services/vpc/vpc_service.py | 6 +- .../services/workspaces/workspaces_service.py | 7 +- .../__init__.py | 0 ...2private_1public_subnets_nat.metadata.json | 30 ++ ...spaces_vpc_2private_1public_subnets_nat.py | 51 ++ .../networkfirewall_in_all_vpc_test.py | 4 + .../aws/services/vpc/vpc_service_test.py | 1 + .../workspaces/workspaces_service_test.py | 1 + ...rkspaces_volume_encryption_enabled_test.py | 4 + ...s_vpc_2private_1public_subnets_nat_test.py | 462 ++++++++++++++++++ 10 files changed, 563 insertions(+), 3 deletions(-) create mode 100644 prowler/providers/aws/services/workspaces/workspaces_vpc_2private_1public_subnets_nat/__init__.py create mode 100644 prowler/providers/aws/services/workspaces/workspaces_vpc_2private_1public_subnets_nat/workspaces_vpc_2private_1public_subnets_nat.metadata.json create mode 100644 prowler/providers/aws/services/workspaces/workspaces_vpc_2private_1public_subnets_nat/workspaces_vpc_2private_1public_subnets_nat.py create mode 100644 tests/providers/aws/services/workspaces/workspaces_vpc_2private_1public_subnets_nat/workspaces_vpc_2private_1public_subnets_nat_test.py diff --git a/prowler/providers/aws/services/vpc/vpc_service.py b/prowler/providers/aws/services/vpc/vpc_service.py index 4c53b922..fb41cecd 100644 --- a/prowler/providers/aws/services/vpc/vpc_service.py +++ b/prowler/providers/aws/services/vpc/vpc_service.py @@ -275,12 +275,14 @@ class VPC: ) ) public = False + nat_gateway = False for route in route_tables_for_subnet.get("RouteTables")[ 0 ].get("Routes"): if "GatewayId" in route and "igw" in route["GatewayId"]: public = True - break + if "NatGatewayId" in route: + nat_gateway = True # Add it to to list of vpc_subnets and to the VPC object object = VpcSubnet( id=subnet["SubnetId"], @@ -290,6 +292,7 @@ class VPC: region=regional_client.region, availability_zone=subnet["AvailabilityZone"], public=public, + nat_gateway=nat_gateway, tags=subnet.get("Tags"), ) self.vpc_subnets[subnet["SubnetId"]] = object @@ -314,6 +317,7 @@ class VpcSubnet(BaseModel): cidr_block: str availability_zone: str public: bool + nat_gateway: bool region: str tags: Optional[list] = [] diff --git a/prowler/providers/aws/services/workspaces/workspaces_service.py b/prowler/providers/aws/services/workspaces/workspaces_service.py index dbc0797d..610d61d8 100644 --- a/prowler/providers/aws/services/workspaces/workspaces_service.py +++ b/prowler/providers/aws/services/workspaces/workspaces_service.py @@ -45,11 +45,13 @@ class WorkSpaces: ) ): workspace_to_append = WorkSpace( - id=workspace["WorkspaceId"], region=regional_client.region + id=workspace.get("WorkspaceId"), + region=regional_client.region, + subnet_id=workspace.get("SubnetId"), ) if ( "UserVolumeEncryptionEnabled" in workspace - and workspace["UserVolumeEncryptionEnabled"] + and workspace.get("UserVolumeEncryptionEnabled") ): workspace_to_append.user_volume_encryption_enabled = True if ( @@ -85,4 +87,5 @@ class WorkSpace(BaseModel): region: str user_volume_encryption_enabled: bool = None root_volume_encryption_enabled: bool = None + subnet_id: str = None tags: Optional[list] = [] diff --git a/prowler/providers/aws/services/workspaces/workspaces_vpc_2private_1public_subnets_nat/__init__.py b/prowler/providers/aws/services/workspaces/workspaces_vpc_2private_1public_subnets_nat/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/prowler/providers/aws/services/workspaces/workspaces_vpc_2private_1public_subnets_nat/workspaces_vpc_2private_1public_subnets_nat.metadata.json b/prowler/providers/aws/services/workspaces/workspaces_vpc_2private_1public_subnets_nat/workspaces_vpc_2private_1public_subnets_nat.metadata.json new file mode 100644 index 00000000..f46a6f99 --- /dev/null +++ b/prowler/providers/aws/services/workspaces/workspaces_vpc_2private_1public_subnets_nat/workspaces_vpc_2private_1public_subnets_nat.metadata.json @@ -0,0 +1,30 @@ +{ + "Provider": "aws", + "CheckID": "workspaces_vpc_2private_1public_subnets_nat", + "CheckTitle": "Ensure that the Workspaces VPC are deployed following the best practices using 1 public subnet and 2 private subnets with a NAT Gateway attached", + "CheckType": [], + "ServiceName": "workspaces", + "SubServiceName": "", + "ResourceIdTemplate": "arn:aws:workspaces:region:account-id:workspace", + "Severity": "medium", + "ResourceType": "AwsWorkspaces", + "Description": "Ensure that the Workspaces VPC are deployed following the best practices using 1 public subnet and 2 private subnets with a NAT Gateway attached", + "Risk": "Proper network segmentation is a key security best practice. Workspaces VPC should be deployed using 1 public subnet and 2 private subnets with a NAT Gateway attached", + "RelatedUrl": "https://docs.aws.amazon.com/workspaces/latest/adminguide/amazon-workspaces-vpc.html", + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "Follow the documentation and deploy Workspaces VPC using 1 public subnet and 2 private subnets with a NAT Gateway attached", + "Url": "https://docs.aws.amazon.com/workspaces/latest/adminguide/amazon-workspaces-vpc.html" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/aws/services/workspaces/workspaces_vpc_2private_1public_subnets_nat/workspaces_vpc_2private_1public_subnets_nat.py b/prowler/providers/aws/services/workspaces/workspaces_vpc_2private_1public_subnets_nat/workspaces_vpc_2private_1public_subnets_nat.py new file mode 100644 index 00000000..32c7ae5b --- /dev/null +++ b/prowler/providers/aws/services/workspaces/workspaces_vpc_2private_1public_subnets_nat/workspaces_vpc_2private_1public_subnets_nat.py @@ -0,0 +1,51 @@ +from prowler.lib.check.models import Check, Check_Report_AWS +from prowler.providers.aws.services.vpc.vpc_client import vpc_client +from prowler.providers.aws.services.workspaces.workspaces_client import ( + workspaces_client, +) + + +class workspaces_vpc_2private_1public_subnets_nat(Check): + def execute(self): + findings = [] + for workspace in workspaces_client.workspaces: + report = Check_Report_AWS(self.metadata()) + report.region = workspace.region + report.resource_id = workspace.id + report.resource_arn = workspace.arn + report.resource_tags = workspace.tags + report.status = "PASS" + report.status_extended = f"Workspace {workspace.id} is in a private subnet within a VPC which has 1 public subnet 2 private subnets with a NAT Gateway attached" + vpc_object = None + is_in_private_subnet = False + if workspace.subnet_id: + if vpc_client.vpcs[vpc_client.vpc_subnets[workspace.subnet_id].vpc_id]: + vpc_object = vpc_client.vpcs[ + vpc_client.vpc_subnets[workspace.subnet_id].vpc_id + ] + if vpc_client.vpc_subnets[workspace.subnet_id]: + if not vpc_client.vpc_subnets[workspace.subnet_id].public: + is_in_private_subnet = True + public_subnets = 0 + private_subnets = 0 + nat_gateway = False + if vpc_object: + for vpc_subnet in vpc_object.subnets: + if vpc_subnet.public: + public_subnets += 1 + if not vpc_subnet.public: + private_subnets += 1 + if vpc_subnet.nat_gateway: + nat_gateway = True + # Check NAT Gateway here + if ( + public_subnets < 1 + or private_subnets < 2 + or not nat_gateway + or not is_in_private_subnet + ): + report.status = "FAIL" + report.status_extended = f"Workspace {workspace.id} is not in a private subnet or its VPC has not 1 public subnet and 2 private subnets with a NAT Gateway attached" + + findings.append(report) + return findings diff --git a/tests/providers/aws/services/networkfirewall/networkfirewall_in_all_vpc/networkfirewall_in_all_vpc_test.py b/tests/providers/aws/services/networkfirewall/networkfirewall_in_all_vpc/networkfirewall_in_all_vpc_test.py index 697d6405..bb52f037 100644 --- a/tests/providers/aws/services/networkfirewall/networkfirewall_in_all_vpc/networkfirewall_in_all_vpc_test.py +++ b/tests/providers/aws/services/networkfirewall/networkfirewall_in_all_vpc/networkfirewall_in_all_vpc_test.py @@ -70,6 +70,7 @@ class Test_networkfirewall_in_all_vpc: cidr_block="192.168.0.0/24", availability_zone="us-east-1a", public=False, + nat_gateway=False, region=AWS_REGION, tags=[], ) @@ -125,6 +126,7 @@ class Test_networkfirewall_in_all_vpc: cidr_block="192.168.0.0/24", availability_zone="us-east-1a", public=False, + nat_gateway=False, region=AWS_REGION, tags=[], ) @@ -190,6 +192,7 @@ class Test_networkfirewall_in_all_vpc: cidr_block="192.168.0.0/24", availability_zone="us-east-1a", public=False, + nat_gateway=False, region=AWS_REGION, tags=[], ) @@ -210,6 +213,7 @@ class Test_networkfirewall_in_all_vpc: cidr_block="192.168.0.0/24", availability_zone="us-east-1a", public=False, + nat_gateway=False, region=AWS_REGION, tags=[], ) diff --git a/tests/providers/aws/services/vpc/vpc_service_test.py b/tests/providers/aws/services/vpc/vpc_service_test.py index a651256b..b3ddd79e 100644 --- a/tests/providers/aws/services/vpc/vpc_service_test.py +++ b/tests/providers/aws/services/vpc/vpc_service_test.py @@ -311,5 +311,6 @@ class Test_VPC_Service: assert vpc.subnets[0].cidr_block == "172.28.7.192/26" assert vpc.subnets[0].availability_zone == f"{AWS_REGION}a" assert vpc.subnets[0].public is False + assert vpc.subnets[0].nat_gateway is False assert vpc.subnets[0].region == AWS_REGION assert vpc.subnets[0].tags is None diff --git a/tests/providers/aws/services/workspaces/workspaces_service_test.py b/tests/providers/aws/services/workspaces/workspaces_service_test.py index 5a851dd9..c1bdc3bf 100644 --- a/tests/providers/aws/services/workspaces/workspaces_service_test.py +++ b/tests/providers/aws/services/workspaces/workspaces_service_test.py @@ -24,6 +24,7 @@ def mock_make_api_call(self, operation_name, kwarg): "WorkspaceId": workspace_id, "UserVolumeEncryptionEnabled": True, "RootVolumeEncryptionEnabled": True, + "SubnetId": "subnet-1234567890", }, ], } diff --git a/tests/providers/aws/services/workspaces/workspaces_volume_encryption_enabled/workspaces_volume_encryption_enabled_test.py b/tests/providers/aws/services/workspaces/workspaces_volume_encryption_enabled/workspaces_volume_encryption_enabled_test.py index 70e7b4a8..87c0f0c5 100644 --- a/tests/providers/aws/services/workspaces/workspaces_volume_encryption_enabled/workspaces_volume_encryption_enabled_test.py +++ b/tests/providers/aws/services/workspaces/workspaces_volume_encryption_enabled/workspaces_volume_encryption_enabled_test.py @@ -35,6 +35,7 @@ class Test_workspaces_volume_encryption_enabled: region=AWS_REGION, user_volume_encryption_enabled=True, root_volume_encryption_enabled=True, + subnet_id="subnet-12345678", ) ) with mock.patch( @@ -64,6 +65,7 @@ class Test_workspaces_volume_encryption_enabled: region=AWS_REGION, user_volume_encryption_enabled=False, root_volume_encryption_enabled=True, + subnet_id="subnet-12345678", ) ) with mock.patch( @@ -91,6 +93,7 @@ class Test_workspaces_volume_encryption_enabled: region=AWS_REGION, user_volume_encryption_enabled=True, root_volume_encryption_enabled=False, + subnet_id="subnet-12345678", ) ) with mock.patch( @@ -118,6 +121,7 @@ class Test_workspaces_volume_encryption_enabled: region=AWS_REGION, user_volume_encryption_enabled=False, root_volume_encryption_enabled=False, + subnet_id="subnet-12345678", ) ) with mock.patch( diff --git a/tests/providers/aws/services/workspaces/workspaces_vpc_2private_1public_subnets_nat/workspaces_vpc_2private_1public_subnets_nat_test.py b/tests/providers/aws/services/workspaces/workspaces_vpc_2private_1public_subnets_nat/workspaces_vpc_2private_1public_subnets_nat_test.py new file mode 100644 index 00000000..ccb65aa6 --- /dev/null +++ b/tests/providers/aws/services/workspaces/workspaces_vpc_2private_1public_subnets_nat/workspaces_vpc_2private_1public_subnets_nat_test.py @@ -0,0 +1,462 @@ +from unittest import mock +from uuid import uuid4 + +from boto3 import client, session +from moto import mock_ec2 + +from prowler.providers.aws.lib.audit_info.models import AWS_Audit_Info +from prowler.providers.aws.services.vpc.vpc_service import VPC +from prowler.providers.aws.services.workspaces.workspaces_service import WorkSpace + +AWS_REGION = "eu-west-1" +AWS_ACCOUNT_NUMBER = "123456789012" +workspace_id = str(uuid4()) + + +class Test_workspaces_vpc_2private_1public_subnets_nat: + 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 + + def test_no_workspaces(self): + workspaces_client = mock.MagicMock + workspaces_client.workspaces = [] + vpc_client = mock.MagicMock + vpc_client.vpcs = [] + with mock.patch( + "prowler.providers.aws.services.workspaces.workspaces_service.WorkSpaces", + workspaces_client, + ): + with mock.patch( + "prowler.providers.aws.services.vpc.vpc_service.VPC", + vpc_client, + ): + from prowler.providers.aws.services.workspaces.workspaces_vpc_2private_1public_subnets_nat.workspaces_vpc_2private_1public_subnets_nat import ( + workspaces_vpc_2private_1public_subnets_nat, + ) + + check = workspaces_vpc_2private_1public_subnets_nat() + result = check.execute() + assert len(result) == 0 + + def test_workspaces_no_subnet(self): + workspaces_client = mock.MagicMock + workspaces_client = mock.MagicMock + workspaces_client.workspaces = [] + workspaces_client.workspaces.append( + WorkSpace( + id=workspace_id, + region=AWS_REGION, + user_volume_encryption_enabled=True, + root_volume_encryption_enabled=True, + ) + ) + + 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, + ): + with mock.patch( + "prowler.providers.aws.services.workspaces.workspaces_vpc_2private_1public_subnets_nat.workspaces_vpc_2private_1public_subnets_nat.vpc_client", + new=VPC(current_audit_info), + ): + with mock.patch( + "prowler.providers.aws.services.workspaces.workspaces_vpc_2private_1public_subnets_nat.workspaces_vpc_2private_1public_subnets_nat.workspaces_client", + new=workspaces_client, + ): + from prowler.providers.aws.services.workspaces.workspaces_vpc_2private_1public_subnets_nat.workspaces_vpc_2private_1public_subnets_nat import ( + workspaces_vpc_2private_1public_subnets_nat, + ) + + check = workspaces_vpc_2private_1public_subnets_nat() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Workspace {workspace_id} is not in a private subnet or its VPC has not 1 public subnet and 2 private subnets with a NAT Gateway attached" + ) + assert result[0].resource_id == workspace_id + assert result[0].resource_arn == "" + + @mock_ec2 + def test_workspaces_vpc_one_private_subnet(self): + # EC2 Client + ec2_client = client("ec2", region_name=AWS_REGION) + vpc = ec2_client.create_vpc( + CidrBlock="172.28.7.0/24", InstanceTenancy="default" + ) + # VPC Private + subnet_private = ec2_client.create_subnet( + VpcId=vpc["Vpc"]["VpcId"], + CidrBlock="172.28.7.0/26", + AvailabilityZone=f"{AWS_REGION}a", + ) + route_table_private = ec2_client.create_route_table( + VpcId=vpc["Vpc"]["VpcId"], + ) + ec2_client.create_route( + DestinationCidrBlock="10.10.10.0", + RouteTableId=route_table_private["RouteTable"]["RouteTableId"], + ) + ec2_client.associate_route_table( + RouteTableId=route_table_private["RouteTable"]["RouteTableId"], + SubnetId=subnet_private["Subnet"]["SubnetId"], + ) + # Workspace Mock + workspaces_client = mock.MagicMock + workspaces_client.workspaces = [] + workspaces_client.workspaces.append( + WorkSpace( + id=workspace_id, + region=AWS_REGION, + user_volume_encryption_enabled=True, + root_volume_encryption_enabled=True, + subnet_id=subnet_private["Subnet"]["SubnetId"], + ) + ) + + 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, + ): + with mock.patch( + "prowler.providers.aws.services.workspaces.workspaces_vpc_2private_1public_subnets_nat.workspaces_vpc_2private_1public_subnets_nat.vpc_client", + new=VPC(current_audit_info), + ): + with mock.patch( + "prowler.providers.aws.services.workspaces.workspaces_vpc_2private_1public_subnets_nat.workspaces_vpc_2private_1public_subnets_nat.workspaces_client", + new=workspaces_client, + ): + from prowler.providers.aws.services.workspaces.workspaces_vpc_2private_1public_subnets_nat.workspaces_vpc_2private_1public_subnets_nat import ( + workspaces_vpc_2private_1public_subnets_nat, + ) + + check = workspaces_vpc_2private_1public_subnets_nat() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Workspace {workspace_id} is not in a private subnet or its VPC has not 1 public subnet and 2 private subnets with a NAT Gateway attached" + ) + assert result[0].resource_id == workspace_id + assert result[0].resource_arn == "" + + @mock_ec2 + def test_workspaces_vpc_two_private_subnet(self): + # EC2 Client + ec2_client = client("ec2", region_name=AWS_REGION) + vpc = ec2_client.create_vpc( + CidrBlock="172.28.7.0/24", InstanceTenancy="default" + ) + # VPC Private + subnet_private = ec2_client.create_subnet( + VpcId=vpc["Vpc"]["VpcId"], + CidrBlock="172.28.7.0/26", + AvailabilityZone=f"{AWS_REGION}a", + ) + route_table_private = ec2_client.create_route_table( + VpcId=vpc["Vpc"]["VpcId"], + ) + ec2_client.create_route( + DestinationCidrBlock="10.10.10.0", + RouteTableId=route_table_private["RouteTable"]["RouteTableId"], + ) + ec2_client.associate_route_table( + RouteTableId=route_table_private["RouteTable"]["RouteTableId"], + SubnetId=subnet_private["Subnet"]["SubnetId"], + ) + # VPC Private 2 + subnet_private_2 = ec2_client.create_subnet( + VpcId=vpc["Vpc"]["VpcId"], + CidrBlock="172.28.7.64/26", + AvailabilityZone=f"{AWS_REGION}a", + ) + route_table_private_2 = ec2_client.create_route_table( + VpcId=vpc["Vpc"]["VpcId"], + ) + ec2_client.create_route( + DestinationCidrBlock="10.10.10.0", + RouteTableId=route_table_private_2["RouteTable"]["RouteTableId"], + ) + ec2_client.associate_route_table( + RouteTableId=route_table_private_2["RouteTable"]["RouteTableId"], + SubnetId=subnet_private_2["Subnet"]["SubnetId"], + ) + # Workspace Mock + workspaces_client = mock.MagicMock + workspaces_client.workspaces = [] + workspaces_client.workspaces.append( + WorkSpace( + id=workspace_id, + region=AWS_REGION, + user_volume_encryption_enabled=True, + root_volume_encryption_enabled=True, + subnet_id=subnet_private["Subnet"]["SubnetId"], + ) + ) + + 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, + ): + with mock.patch( + "prowler.providers.aws.services.workspaces.workspaces_vpc_2private_1public_subnets_nat.workspaces_vpc_2private_1public_subnets_nat.vpc_client", + new=VPC(current_audit_info), + ): + with mock.patch( + "prowler.providers.aws.services.workspaces.workspaces_vpc_2private_1public_subnets_nat.workspaces_vpc_2private_1public_subnets_nat.workspaces_client", + new=workspaces_client, + ): + from prowler.providers.aws.services.workspaces.workspaces_vpc_2private_1public_subnets_nat.workspaces_vpc_2private_1public_subnets_nat import ( + workspaces_vpc_2private_1public_subnets_nat, + ) + + check = workspaces_vpc_2private_1public_subnets_nat() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Workspace {workspace_id} is not in a private subnet or its VPC has not 1 public subnet and 2 private subnets with a NAT Gateway attached" + ) + assert result[0].resource_id == workspace_id + assert result[0].resource_arn == "" + + @mock_ec2 + def test_workspaces_vpc_two_private_subnet_one_public(self): + # EC2 Client + ec2_client = client("ec2", region_name=AWS_REGION) + vpc = ec2_client.create_vpc( + CidrBlock="172.28.7.0/24", InstanceTenancy="default" + ) + # VPC Private + subnet_private = ec2_client.create_subnet( + VpcId=vpc["Vpc"]["VpcId"], + CidrBlock="172.28.7.0/26", + AvailabilityZone=f"{AWS_REGION}a", + ) + route_table_private = ec2_client.create_route_table( + VpcId=vpc["Vpc"]["VpcId"], + ) + ec2_client.create_route( + DestinationCidrBlock="10.10.10.0", + RouteTableId=route_table_private["RouteTable"]["RouteTableId"], + ) + ec2_client.associate_route_table( + RouteTableId=route_table_private["RouteTable"]["RouteTableId"], + SubnetId=subnet_private["Subnet"]["SubnetId"], + ) + # VPC Private 2 + subnet_private_2 = ec2_client.create_subnet( + VpcId=vpc["Vpc"]["VpcId"], + CidrBlock="172.28.7.64/26", + AvailabilityZone=f"{AWS_REGION}a", + ) + route_table_private_2 = ec2_client.create_route_table( + VpcId=vpc["Vpc"]["VpcId"], + ) + ec2_client.create_route( + DestinationCidrBlock="10.10.10.0", + RouteTableId=route_table_private_2["RouteTable"]["RouteTableId"], + ) + ec2_client.associate_route_table( + RouteTableId=route_table_private_2["RouteTable"]["RouteTableId"], + SubnetId=subnet_private_2["Subnet"]["SubnetId"], + ) + # VPC Public + subnet_public = ec2_client.create_subnet( + VpcId=vpc["Vpc"]["VpcId"], + CidrBlock="172.28.7.192/26", + AvailabilityZone=f"{AWS_REGION}a", + ) + route_table_public = ec2_client.create_route_table( + VpcId=vpc["Vpc"]["VpcId"], + ) + igw = ec2_client.create_internet_gateway() + ec2_client.create_route( + DestinationCidrBlock="0.0.0.0", + RouteTableId=route_table_public["RouteTable"]["RouteTableId"], + GatewayId=igw["InternetGateway"]["InternetGatewayId"], + ) + ec2_client.associate_route_table( + RouteTableId=route_table_public["RouteTable"]["RouteTableId"], + SubnetId=subnet_public["Subnet"]["SubnetId"], + ) + # Workspace Mock + workspaces_client = mock.MagicMock + workspaces_client.workspaces = [] + workspaces_client.workspaces.append( + WorkSpace( + id=workspace_id, + region=AWS_REGION, + user_volume_encryption_enabled=True, + root_volume_encryption_enabled=True, + subnet_id=subnet_private["Subnet"]["SubnetId"], + ) + ) + + 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, + ): + with mock.patch( + "prowler.providers.aws.services.workspaces.workspaces_vpc_2private_1public_subnets_nat.workspaces_vpc_2private_1public_subnets_nat.vpc_client", + new=VPC(current_audit_info), + ): + with mock.patch( + "prowler.providers.aws.services.workspaces.workspaces_vpc_2private_1public_subnets_nat.workspaces_vpc_2private_1public_subnets_nat.workspaces_client", + new=workspaces_client, + ): + from prowler.providers.aws.services.workspaces.workspaces_vpc_2private_1public_subnets_nat.workspaces_vpc_2private_1public_subnets_nat import ( + workspaces_vpc_2private_1public_subnets_nat, + ) + + check = workspaces_vpc_2private_1public_subnets_nat() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Workspace {workspace_id} is not in a private subnet or its VPC has not 1 public subnet and 2 private subnets with a NAT Gateway attached" + ) + assert result[0].resource_id == workspace_id + assert result[0].resource_arn == "" + + @mock_ec2 + def test_workspaces_vpc_two_private_subnet_one_public_and_nat(self): + # EC2 Client + ec2_client = client("ec2", region_name=AWS_REGION) + vpc = ec2_client.create_vpc( + CidrBlock="172.28.7.0/24", InstanceTenancy="default" + ) + # VPC Private + subnet_private = ec2_client.create_subnet( + VpcId=vpc["Vpc"]["VpcId"], + CidrBlock="172.28.7.0/26", + AvailabilityZone=f"{AWS_REGION}a", + ) + route_table_private = ec2_client.create_route_table( + VpcId=vpc["Vpc"]["VpcId"], + ) + ec2_client.create_route( + DestinationCidrBlock="10.10.10.0", + RouteTableId=route_table_private["RouteTable"]["RouteTableId"], + ) + ec2_client.associate_route_table( + RouteTableId=route_table_private["RouteTable"]["RouteTableId"], + SubnetId=subnet_private["Subnet"]["SubnetId"], + ) + # VPC Private 2 + subnet_private_2 = ec2_client.create_subnet( + VpcId=vpc["Vpc"]["VpcId"], + CidrBlock="172.28.7.64/26", + AvailabilityZone=f"{AWS_REGION}a", + ) + route_table_private_2 = ec2_client.create_route_table( + VpcId=vpc["Vpc"]["VpcId"], + ) + ec2_client.create_route( + DestinationCidrBlock="10.10.10.0", + RouteTableId=route_table_private_2["RouteTable"]["RouteTableId"], + ) + ec2_client.associate_route_table( + RouteTableId=route_table_private_2["RouteTable"]["RouteTableId"], + SubnetId=subnet_private_2["Subnet"]["SubnetId"], + ) + # Nat Gateway + nat_gw = ec2_client.create_nat_gateway( + SubnetId=subnet_private_2["Subnet"]["SubnetId"], + ) + ec2_client.create_route( + NatGatewayId=nat_gw["NatGateway"]["NatGatewayId"], + RouteTableId=route_table_private_2["RouteTable"]["RouteTableId"], + ) + # VPC Public + subnet_public = ec2_client.create_subnet( + VpcId=vpc["Vpc"]["VpcId"], + CidrBlock="172.28.7.192/26", + AvailabilityZone=f"{AWS_REGION}a", + ) + route_table_public = ec2_client.create_route_table( + VpcId=vpc["Vpc"]["VpcId"], + ) + igw = ec2_client.create_internet_gateway() + ec2_client.create_route( + DestinationCidrBlock="0.0.0.0", + RouteTableId=route_table_public["RouteTable"]["RouteTableId"], + GatewayId=igw["InternetGateway"]["InternetGatewayId"], + ) + ec2_client.associate_route_table( + RouteTableId=route_table_public["RouteTable"]["RouteTableId"], + SubnetId=subnet_public["Subnet"]["SubnetId"], + ) + # Workspace Mock + workspaces_client = mock.MagicMock + workspaces_client.workspaces = [] + workspaces_client.workspaces.append( + WorkSpace( + id=workspace_id, + region=AWS_REGION, + user_volume_encryption_enabled=True, + root_volume_encryption_enabled=True, + subnet_id=subnet_private["Subnet"]["SubnetId"], + ) + ) + + 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, + ): + with mock.patch( + "prowler.providers.aws.services.workspaces.workspaces_vpc_2private_1public_subnets_nat.workspaces_vpc_2private_1public_subnets_nat.vpc_client", + new=VPC(current_audit_info), + ): + with mock.patch( + "prowler.providers.aws.services.workspaces.workspaces_vpc_2private_1public_subnets_nat.workspaces_vpc_2private_1public_subnets_nat.workspaces_client", + new=workspaces_client, + ): + from prowler.providers.aws.services.workspaces.workspaces_vpc_2private_1public_subnets_nat.workspaces_vpc_2private_1public_subnets_nat import ( + workspaces_vpc_2private_1public_subnets_nat, + ) + + check = workspaces_vpc_2private_1public_subnets_nat() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"Workspace {workspace_id} is in a private subnet within a VPC which has 1 public subnet 2 private subnets with a NAT Gateway attached" + ) + assert result[0].resource_id == workspace_id + assert result[0].resource_arn == ""